首页
更多应用
Search
1
修改iview的标签为i-的形式而不是驼峰的形式
2,791 阅读
2
PHP微信和企业微信签名
2,522 阅读
3
在VUE中怎么全局引入sass文件
2,223 阅读
4
vscode硬件占用较高解决方案
2,017 阅读
5
解决Macos下storm系列IDE卡顿的问题
1,975 阅读
默认分类
JS
VUE
CSS
mac使用技巧
React
fastmock
登录
/
注册
Search
标签搜索
react
js
vue
vscode
nodejs
项目
代码
webpack
工具
nginx
小程序
css
fastmock
eslint
npm
http
vue-cli3
git
浏览器
const
fastmock技术社区
累计撰写
102
篇文章
累计收到
26
条评论
首页
栏目
默认分类
JS
VUE
CSS
mac使用技巧
React
fastmock
页面
更多应用
搜索到
3
篇与
的结果
前端性能优化之webpack打包优化
前端工程化彻底盛行的今天,我们已经习惯使用打包工具来帮助我们打包代码到最终能在浏览器运行的js或者css代码,这样我们就可以在编写代码时放心地使用所有的高级语法,其中最让前端coder感到爽快的就是 import export,我们不再需要像以前一样在html里面放很多很多script。或者使用amd。cmd,requirejs工具来写模块引用的代码,这些方便,也让我们很容易忽略一个问题,就是打包的产物的大小,当一个项目足够大时,我们的js甚至可以达到几MB到几十MB,所以,今天就来总结下关于减小构建产物体积,来达到减少首屏加载时间的内容webpack 官方自带的优化策略 https://www.webpackjs.com/configuration/optimization/这里以react项目为例,列举需要优化的构建项一、使用代码拆分,让我们的页面代码构建到单独的js,首次访问页面的时候才加载这块jsmodule.exports = { optimization: { { usedExports: true, concatenateModules: false, chunkIds: 'deterministic', runtimeChunk: true, // 将运行时依赖单独打包-运行时依赖如我们使用的async await语法所需的降级兼容代码 设置为 'single' 则所有的runtime依赖打包到一个文件 // 使用代码拆分 参考文档 https://www.51cto.com/article/689344.html splitChunks: { chunks: 'async', // webpack 打包chunk分为 entry chunk 和async chunk两种,配置文件中的entry配置的主包是默认拆分的,多个入口,多个 main chunk。async chunk就是使用import('./xxx.js') 一步模块加载方法加载的模块。那么 chunks选项就是指定这两种chunk哪些需要分包的,`initial` 只分包主包, async 只分包异步加载的包。all 分包上面两种包,这里要注意的就是all有时候会理解成“所有”就会以为所有使用了import './xxx.js'引入的包都会被分包 minSize: 20, // 超过了这个大小的包才会被拆分 minRemainingSize: 0, minChunks: 1, // 被引用次数大于这个数的包才会被拆分,这里要注意的是,被引用是只命中entry chunk 和 async chunk 的引用者才算 maxAsyncRequests: 30, maxInitialRequests: 30, enforceSizeThreshold: 100, // 超过这个大小的包,不管有没有命中上面的配置,都分包 // 对指定规则的文件使用特定的分包策略 cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, // 匹配文件路径 type:/\.json$/, // 匹配文件类型 idHint:'vendors',// 用于设置 Chunk ID,它还会被追加到最终产物文件名中,例如 idHint = 'vendors' 时,输出产物文件名形如 vendors-xxx-xxx.js minChunks: 1, minSize: 0, priority: 2 // 设置优先级,如果文件命中多个groups策略,优先使用这个配置数字较大的规则组 } } } } } }接下来,在react路由里,将组件引入代码 import Xxxx from '@src/routes/Xxxx' 修改为如下引用方式//该组件是动态加载的 千万注意,因为组件是动态加载的,那么。就有可能出现加载失败或者加载错误的情况,所以需要使用 Suspense 组件来包裹,组件还未加载,显示fallback中的内容,组件加载完成,显示组件,加载失败会throw一个error,防止页面崩溃 const Home = React.lazy(() => import('./Home')); function Layout() { return ( // 显示 <Spinner> 组件直至 Home 加载完成 <React.Suspense fallback={<Spinner />}> <div> <Home /> </div> </React.Suspense> ); }上面的分包策略的理解注释中的内容提到了分包的条件和规则,那么,为了尽可能减小我们的主包的大小,我们就要尽可能减少在我们的 entry 选项中指定的入口文件中对其他模块的引用,或者使用异步模块引用的方式,常见的几个优化项目为优化使用到的工具的引用,将必要的工具引用单独提到一个文件中,避免打包其他没用到的代码到主包有些应用初始化相关但是跟主应用无关的代码,使用异步模块加载,如下// app.ts (async () => { const {default: AppInit} = await import('./app-init'); aegis = AppInit.tam(); AppInit.dataInsight(); AppInit.chunkError(); })();如果在入口文件中有react或者vue路由使用的组件,使用react或vue提供的异步路由方法引入使用二、将三方库通过CDN引入而不打包到我们的代码包默认情况下,我们一般都会将我们所需要的依赖,例如react,moment,axios等三方包通过npm或yarn安装到本地,然后直接import进来使用,这种方式势必就会将这些第三方包打包到我们自己的js中,且因为这些库本身体积就较大,所以会导致我们打包出来的js非常大,而且,当我们使用了chunk切分后,各个chunk都会单独打包进去这些依赖内容。针对这种情况,webpack提供了 externals 选项来让我们可以从外部获取这些扩展依赖,首先,我们需要通过script标签的形式来引入我们需要使用的三方库,有两种方式,一种是手动在 html-webpack-plugin 的html模板文件或者content内容中加入script标签,第二种是使用html-webpack-tags-plugin插件,通过配置的方式往html内容中动态插入script标签,这里推荐后者,原因是方便写判断逻辑,而不是在html中通过ejs模板语法来写判断逻辑然后,配置externals选项告诉webpack当我们使用import语句导入模块时,实际使用的是是什么内容(一般三方库都会导出一个包含了所有他包含内容的全局变量)const assetsPath = 'https://static.xxx.com/js'; module.exports = { externals: isDev ? {} : { // 排除不打包 'react': 'React', 'react-dom': 'ReactDOM', 'react-router': 'ReactRouter', 'react-router-dom': 'ReactRouterDOM', 'axios': 'axios', 'moment': 'moment', 'moment-timezone': 'moment', 'lodash': '_', }, plugins: [ ...config.plugins, new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn|ja|ko/), new webpack.DefinePlugin(envKeys), // 开发环境不使用这种方式,因为会影响本地开发的热更新 new HtmlWebpackTagsPlugin({ tags: isDev ? [] : [ { type: 'js', path: '/react-16.11.0.production.min.js', attributes: { defer: 'defer' }, // defer: load完成后不立即执行,等带页面DOMLoaded事件执行前执行,等价于把script放到所有dom之后 publicPath: assetsPath, append: false, }, { type: 'js', path: '/react-dom-16.11.0.production.min.js', attributes: { defer: 'defer' }, publicPath: assetsPath, append: false, }, { type: 'js', path: '/react-router-5.2.1.min.js ', attributes: { defer: 'defer' }, publicPath: assetsPath, append: false, }, { type: 'js', path: '/react-router-dom-5.2.1.min.js', attributes: { defer: 'defer' }, publicPath: assetsPath, append: false, }, { type: 'js', path: '/axios-0.26.0.min.js', attributes: { defer: 'defer' }, publicPath: assetsPath, append: false, }, { type: 'js', path: '/moment.min.js', attributes: { defer: 'defer' }, publicPath: assetsPath, append: false, }, { type: 'js', path: '/lodash-4.17.21.min.js ', attributes: { defer: 'defer' }, publicPath: assetsPath, append: false, }, { type: 'js', path: '/moment-timezone-with-data-10-year-range.min.js', attributes: { defer: 'defer' }, publicPath: assetsPath, append: true, }, ], }), new CopyWebpackPlugin({ patterns: [ { from: 'public', globOptions: { ignore: ['**/index.html'], }, to: 'dist', }, ], }), ].concat(!isDev ? [new BundleAnalyzerPlugin({analyzerPort: 8889, analyzerMode: 'static'})] : []), }
2023年12月14日
137 阅读
0 评论
0 点赞
2023-06-30
chrome浏览器扩展v3版本配置项整理备忘
manifest.json配置文件{ //chrome插件的版本 "manifest_version": 3, //插件名称 "name": "ChromeName", //插件版本号 "version": "1.0.0", //插件描述,Plugin_Desc是多语言的key,chrome插件支持多语言配置,__MSG_xxx__ "description": "__MSG_Plugin_Desc__", //默认语言(如果当前浏览器设置的语言不存在多语言配置文件,则默认中文),Chrome插件的多语言只能根据当前浏览器设置的语言来设定,无法通过代码更改语言 "default_locale": "zh_CN", //内容安全政策,V2的value是字符串,V3是对象 "content_security_policy": { //原文:此政策涵盖您的扩展程序中的页面,包括 html 文件和服务人员;具体不是很明白,但是参数值得是self,即当前自己 "extension_pages": "script-src 'self'; object-src 'self'", //原文:此政策涵盖您的扩展程序使用的任何[沙盒扩展程序页面];具体不是很明白,但是参数值得是self,即当前自己 "sandbox": "sandbox allow-scripts; script-src 'self'; object-src 'self'" }, //key,发布插件后会给一个key,把那个key的值放这里 "key": "xxx", //icon,浏览器扩展程序管理里面的图标、浏览器右侧插件图标点开的下拉菜单展示的已开启插件的图标、以及插件详情页的标签卡的那个小图标 "icons": { "16": "static/img/logo-16.png", "19": "static/img/logo-19.png", "38": "static/img/logo-38.png", "48": "static/img/logo-48.png", "128": "static/img/logo-128.png" }, //背景页,后台脚本引入,v2是scripts:[xxx,xxx],可以引入多个js文件,v3是service_worker:'xxx',只能引入一个js,v3版最大的改动应该就是这里了,扩展程序管理界面的插件的那个“背景页”也将变成“Service Worker”,改动之后background.js将和浏览器完全分离,即无法调用window和ducoment对象 //可以看介绍: //1、//developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#background-service-workers; //2、//developer.chrome.com/docs/extensions/mv3/migrating_to_service_workers/ "background": { "service_worker": "background.js" }, //注入脚本,值是个数组对象,可以有多个对象 "content_scripts": [ //每个对象代表一个注入的配置 { //需要在指定页面注入的js脚本文件 "js": [ "xxx.js", "xxx.js", ], //需要注入js脚本文件的指定页面 "matches": [ "https://*.csdn.net/*", "https://*.xxx.com/*" ], //不允许注入js脚本文件的指定页面 "exclude_matches": ["https://*.xxx.com/*"], //什么时候注入的js脚本,document_start=页面加载开始时,document_end=页面加载结束时 "run_at": "document_end" } ], //API权限,需要使用某些API时需要设置该API权限才行 "permissions": [ "contextMenus", //自定义创建右键菜单API "tabs", //tab选项卡API "storage", //缓存API "webRequest", //监听浏览器请求API ... ], //主机权限,在背景页backgroud.js里面或者popup页面走请求时,请求域名的白名单权限,如果没添加的则请求会失败 "host_permissions": [ "https://*.csdn.net/*", "https://*.xxx.com/*" ], //动作API,原文:在 Manifest V2 中,有两种不同的 API 来实现操作: `"browser_action"` 和 `"page_action"` . //这些 API 在引入时扮演了不同的角色,但随着时间的推移它们变得多余,因此在 Manifest V3 中,我们将它们统一为单个 `"action"` API; //配置上action:{},可以是空对象,但是action这个配置得有,不然的话扩展程序管理界面的“Service Worker”将显示无效, //且无法点开“Service Worker”的开发者工具控制台以及点击插件图标时触发的这个方法会报错:chrome.action.onClicked.addListener, "action": { }, //通过网络访问的资源,v2是提供一个数组,v3得提供数组对象,每个对象可以映射到一组资源到一组 URL 或扩展 ID "web_accessible_resources": [{ //允许访问的资源路径,数组传多个参数 "resources": ["*/img/xxx.png", "*/img/xxx2.png"], //允许访问资源的页面 "matches": [ "https://*.csdn.net/*", "https://*.xxx.com/*" ] }] }消息监听1、插件内部发送消息//onMessage消息监听 chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { console.log(request.text); //打印出来的值:“我是个测试内容” sendResponse('触发成功了'); //返回一个内容到发送消息的回调函数中 }); //发送消息,触发上面的onMessage chrome.runtime.sendMessage('', {text: '我是个测试内容'}, function (msg) { console.log(msg); //打印的内容是:“触发成功了” });2、除了在插件内部contenscript background 和 popup之间传递消息以外,其他网站也可以给插件发送消息。方法如下首先,需要增加配置 externally_connectable:{matches:[“https://*.xxx.com/”]}指定允许哪些网站可以给当前插件发送消息,相当于白名单,只有在白名单中的站点发送的消息,扩展才会监听//onMessageExternal消息监听 chrome.runtime.onMessageExternal.addListener(function (request, sender, sendResponse) { console.log(request.text); //打印出来的值:“我是个测试内容” sendResponse('外部触发成功'); //返回一个内容到发送消息的回调函数中 }); //发送消息,触发上面的onMessageExternal //第一个参数是插件Id,指定要发送给哪个插件 //第二个参数是想要传给插件的数据信息 //第三个是让插件那边调用的回调函数,触发回来 chrome.runtime.sendMessage('chromeId', {text: '我是个测试内容'}, function (msg) { console.log(msg); //打印的内容是:"外部触发成功" });内部发送sendMessage不会触发到onMessageExternal,外部发送sendMessage不会触发到onMessage
2023年06月30日
716 阅读
0 评论
1 点赞
2023-01-12
使用ua-parser库处理userAgent判断微信授权登录支持的浏览器
平时我们在js中判断浏览器名称和版本都是通过解析userAgent字符串来判断,但是自己挨着用字符串判断始终不能保证处理的准确性,这种时候就可以考虑找找社区有没有比较成熟的解析库了,成熟的库优势就是经过比较漫长的迭代沉淀,且根据用户反馈做了更多的优化调整。对于ua解析库,比较成熟的就是 ua-parser 了,ua-parser 有很多语言版本,支持 python php golang 等,前端直接使用 ua-parser-js 就可以了import UAParser from 'ua-parser-js'; const WECHAT_SUPPORT_DEVICE = [ { // 微信内 webview browser: 'wechat', }, { // uc浏览器 browser: 'ucbrowser', }, { // qq 浏览器 browser: 'qqbrowser', }, { // ios safari 浏览器 browser: 'mobile safari', os: 'ios', }, ]; function isWechatSupportDevice(): boolean { const parser = new UAParser(navigator.userAgent); // you need to pass the user-agent for nodejs const parserResults = parser.getBrowser(); const browserName = parserResults.name; console.log(browserName); const isValid = WECHAT_SUPPORT_DEVICE.some((item) => item.browser === browserName.toLowerCase()); return isValid; },
2023年01月12日
315 阅读
0 评论
0 点赞