首页
更多应用
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-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 点赞
2022-11-23
webpack5.x+React18搭建移动端项目
webpack5.x+React18搭建移动端项目项目仓库地址 https://github.com/MarvenGong/webpack5-react18-ts-basic在项目开发中,每开始一个新项目我都是使用现有的脚手架,这非常便于快速地启动一个新项目,而且通用的脚手架通常考虑地更加全面,也有利于项目的稳定开发;不过对于一个小项目,根据需求自己搭建可能会更好,一方面小项目不需要脚手架那么丰富的功能,另一方面可以提高对项目的掌控度以方便后期的扩展本项目基于公司内一个移动端项目搭建,旨在搭建一个快速,高效,灵活的前端开发环境。项目中使用了最新版本的webpack构建工具,最新版本的react前段框架和react-router6.x特性搭建,通过本项目,可以可以熟悉和巩固以下基础技能webpack5构建原理和基本配置,以及打包性能的提升和使用方法react18的feature,如,根组件的创建方式等const root = createRoot(document.getElementById('root') as Element); root.render(<RouterProvider router={router} />);react-router6 的新特性和单文件路由的使用方式wecpack5 中配置移动端响应式方案 rem布局的方法其他webpack插件和loader的使用项目仓库地址 https://github.com/MarvenGong/webpack5-react18-ts-basic接下来,详细说明项目中的各个配置环境webpack5的一个重要特性,会根据配置的mode属性是develop还是production来判断是否需要开启构建优化,这个优化过程是指对构建产品的优化,如js uglify,压缩,tree-shaking 等等,所以,我们的开发环境,是可以不用用到那些优化操作的,那么就需要把mode指定成 develop 这样可以在我们保存代码后,获得更快的热更新速度,我这里为了简单,直接在npm script中制定env,在webpack.js中使用package.json 中的scripts"scripts": { "serve": "cross-env NODE_ENV=development webpack serve --config webpack.config.js", "dev": "npm run serve", },PS: 切忌在NODE_ENV=development顺手写上&&写上了就设置不上了webpack.config.js 中使用环境变量const isDev = process.env.NODE_ENV === 'development'; mode: isDev ? 'development' : 'production',基础配置1、entry入口,配置webpack构建的入口文件我这里使用了package.json 中的name属性的值作为主文件的名称entry: { [packageJson?.name]: path.resolve(__dirname, './src/app.tsx'), },2、output 制定输出文件路径和文件名output: { // 开发环境不使用hash文件名 filename: isDev ? '[name].js' : '[name].[hash].js', path: path.resolve(__dirname, './dist'), publicPath: '/', clean: true, // 输出文件前会先清空目标目录的文件 },3、stats 指定webpack在控制台的输出内容stats: { all: false, assets: true, errors: true, assetsSort: '!size', // 按照文件大小倒序 entrypoints: true, modules: false, assetsSpace: 1000, preset: 'minimal', },4、devServer 配置本地开发服务器,如端口,代理等,此处不做详细说明,5、devtool 制定sourcemap文件生成方式devtool: isDev ? 'inline-source-map' : 'source-map', // 开发环境直接打包到js文件中,现网环境打包到独立文件,我的处理方式是在自动化部署工具中将sourcemap文件保存到特定地方,线上排查的时候使用6、resolve 配置6-1 extensions 指定webpack需要处理的文件扩展名类型6-2 alias 路径别名,注意要去 tsconfig 中相应配置,否则代码中会报ts错误项目使用的loader处理module说明ts-loader 处理typescript 语法以及es6特性(包含了babel-loader)style-loader 处理样式资源到html中,开发环境使用MiniCssExtractPlugin.loader 处理样式资源到单独的样式文件中,生产环境使用css-loader 让我们可以在js(x)或者ts(x)中使用import语句导入样式文件 如 import ‘./style.less’postcss-loader 对css进行编译处理,本项目主要用来处理 px2rem 将我们的样式中的px转为rem(只能针对import导入的样式文件,行内样式不生效)less-loader less编译为cssurl-loader 处理图片和字体等资源,小于limit则编码,大于则处理路径,包含了 file-loader(file-loader 主要作用是使我们可以在js(x)或者ts(x)中使用import语句导入静态资源文件 import ‘./xxx.png’)使用到的plugins做打包后处理说明copy-webpack-plugin 拷贝 public下的静态资源到 dist 目录clean-webpack-plugin 清理打包目标目录,防止重复文件WebpackBar 美化打包进度显示的插件HtmlWebpackPlugin 处理我们的构建产物到指定的html中,重中之重MiniCssExtractPlugin 压缩我们的css文件,只在生产环境使用好了,有了上面的介绍,就可以直接看我们的webpack配置文件了主webpack.config.js// @ts-nocheck const path = require('path'); const tsImportPluginFactory = require('ts-import-plugin'); const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const smp = new SpeedMeasurePlugin(); const fs = require('fs'); const packageJson = require('./package.json'); const optimization = require('./webpack-configs/optimization'); const config = require('./webpack-configs/config'); const plugins = require('./webpack-configs/plugins'); const isDev = process.env.NODE_ENV === 'development'; const SMP_SWICTH = false; console.log('-------当前环境-------', process.env.NODE_ENV); const wrapConfig = (isDev && SMP_SWICTH) ? smp.wrap : (config) => config; module.exports = wrapConfig({ target: 'web', mode: isDev ? 'development' : 'production', entry: { [packageJson?.name]: path.resolve(__dirname, './src/app.tsx'), }, output: { filename: isDev ? '[name].js' : '[name].[hash].js', path: path.resolve(__dirname, './dist'), publicPath: '/', clean: true, }, // infrastructureLogging: { // level: 'error' // }, stats: { all: false, assets: true, errors: true, assetsSort: '!size', entrypoints: true, modules: false, assetsSpace: 1000, preset: 'minimal', }, devServer: { ...config.dev, historyApiFallback: true, // static: { // directory: path.join(__dirname, './public'), // }, watchFiles: './src/**/*', }, devtool: isDev ? 'inline-source-map' : 'source-map', resolve: { alias: { '@src': path.resolve(__dirname, './src'), '@tea/app': path.resolve(__dirname, './app'), }, extensions: ['.ts', '.tsx', '.js', 'less'], }, module: { rules: [ { test: /\.(jsx|tsx|js|ts)$/, loader: 'ts-loader', options: { transpileOnly: true, getCustomTransformers: () => ({ before: [ tsImportPluginFactory([ { style: false, libraryName: 'lodash', libraryDirectory: null, camel2DashComponentName: false, }, { style: true }, ]), ], }), compilerOptions: { module: 'esnext', }, }, exclude: /node_modules/, }, { test: /\.(le|c)ss$/, use: [ isDev ? 'style-loader' : MiniCssExtractPlugin.loader, // 现网环境才提取css到一个文件中 { loader: 'css-loader', // 使用import语句导入样式 }, { loader: "postcss-loader", options: { postcssOptions: { config: './postcss.config.js', }, sourceMap: true, } }, { loader: 'less-loader', // 转less为css options: { lessOptions: { modifyVars: { '@body-background': '#f5f5f5', }, javascriptEnabled: true, }, }, }, // { // loader: 'style-resources-loader', // options: { // patterns: path.resolve(__dirname, './src/styles/common.less'), // 全局引入公共的scss 文件 // }, // }, ], }, { test: /\.(png|jp(e)?g|gif|woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, use: [ { loader: 'url-loader', // 处理图片和字体等资源,小于limit则编码,大于则处理路径,包含了 file-loader options: { limit: 8192, }, }, ], }, ], }, watchOptions: { // 设置不监听的⽂件或⽂件夹,默认为空 ignored: /node_modules/, // ⽂件改变不会⽴即执⾏,⽽是会等待300ms之后再去执⾏ aggregateTimeout: 300, // 原理是轮询系统⽂件有⽆变化再去更新的,默认1秒钟轮询1000次 poll: 1000, }, plugins, optimization: !isDev ? optimization : {}, }); 在根目录下的wenpack-configs 目录中的下面三个拆出来的jswebpack 插件配置 plugins.jsconst path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const WebpackBar = require('webpackbar'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const chalk = require('chalk'); const webpack = require('webpack'); const isDev = process.env.NODE_ENV === 'development'; const config = require('./config'); module.exports = [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public/px2rem-hike.js') } ], }), new CleanWebpackPlugin({ dry: false, // 是否打印删除的内容 }), // 热更新替换 new webpack.HotModuleReplacementPlugin(), // @ts-ignore new WebpackBar({ name: '能碳工场移动端h5', color: '#0049b0', // 默认green,进度条颜色支持HEX basic: true, // 默认true,启用一个简单的日志报告器 reporter: { change() { console.log(chalk.blue.bold('文件修改,重新编译...')); }, afterAllDone(context) { console.log(chalk.bgBlue.white(' 能碳工场移动端 ') + chalk.green(' 编译完成')); isDev && console.log(chalk.bgBlue.white(' 能碳工场移动端 ') + chalk.green(' 开发预览地址:http://127.0.0.1:' + config.dev.port)) }, }, }), new HtmlWebpackPlugin({ minify: false, chunks: 'all', template: path.resolve(__dirname, '../public/index.html'), filename: 'index.html' }), new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn|ja|ko/), ].concat(isDev ? [] : [new MiniCssExtractPlugin({ filename: '[name].[hash].css', })])webpack优化配置 optimization.jsconst CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); module.exports = { minimize: true, minimizer: [ new CssMinimizerPlugin(), '...' // 压缩css 和 js ], splitChunks: { chunks: 'async', cacheGroups: { defaultVendors: { test: /[\\/]node_modules[\\/]/, priority: -10, reuseExistingChunk: true, }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true, }, }, }, }; 其他常用需要修改配置 config.jsmodule.exports = { dev: { host: '0.0.0.0', port: 10010, hot: true, open: false, client: { overlay: { errors: true, warnings: false, }, }, proxy: { '/api': { // logLevel: 'debug', changeOrigin: true, pathRewrite: { '^/api': '' }, target: 'http://30.168.123.79', }, }, } };
2022年11月23日
484 阅读
4 评论
1 点赞