이번에는 Vue 패키징 최적화를 위한 코드 분할 단계에 대해 자세히 설명하겠습니다. Vue 패키징 최적화를 위한 코드 분할의 주의사항은 무엇인가요?
http1 시대에 가장 일반적인 성능 최적화 중 하나는 http 요청 수를 병합하는 것입니다. 일반적으로 우리는 많은 js 코드를 병합하지만, js 패키지의 크기가 특히 큰 경우에는 성능을 향상시키기가 약간 어렵습니다. 그리고 모든 코드를 합리적으로 분할했다면 첫 번째 화면과 첫 번째 화면이 아닌 코드를 떼어내고 비즈니스 코드와 기본 라이브러리 코드를 분할한 다음 필요할 때 특정 코드 조각을 로드하고, 다음에 필요하면 다음 번에 로드합니다. 다시 사용하고, 캐시에서 읽으세요. 첫째, 브라우저 캐시를 더 잘 활용할 수 있습니다. 둘째, 첫 번째 화면의 로딩 속도를 향상시켜 사용자 경험을 크게 향상시킬 수 있습니다.
핵심 아이디어
비즈니스 코드와 기본 라이브러리의 분리
이것은 실제로 이해하기 쉽습니다. 비즈니스 코드는 일반적으로 자주 업데이트되고 반복되는 반면 기본 라이브러리는 일반적으로 여기서 분할하면 쉽게 만들 수 있습니다. 브라우저 캐시를 완전히 사용합니다. 기본 라이브러리 코드를 로드합니다.
주문형 비동기 로딩
이것은 주로 첫 번째 화면 요청 크기 문제를 해결합니다. 첫 번째 화면에 액세스할 때 모든 라우팅 코드를 로드하는 대신 첫 번째 화면에 필요한 로직만 로드하면 됩니다.
실용적인 전투
최근에는 vuetify를 사용하여 내부 시스템을 변환했는데 처음에는 가장 일반적으로 사용되는 웹팩 구성을 사용했는데 기능이 빠르게 개발되었지만 패키징하고 나니 효과가 나타났습니다. 명확하지 않았고, 많은 대형 패키지가 생성되었습니다
여기서 우리는 webpack-bundle-analyzer를 사용하여 vue 및 vuetify와 같은 모듈이 반복적으로 사용되는 것을 볼 수 있습니다. 포장.
여기서 구성을 먼저 게시하고 나중에 분석에 사용할 것입니다.
const path = require('path') const webpack = require('webpack') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const generateHtml = new HtmlWebpackPlugin({ title: '逍遥系统', template: './src/index.html', minify: { removeComments: true } }) module.exports = { entry: { vendor: ['vue', 'vue-router', 'vuetify'], app: './src/main.js' }, output: { path: path.resolve(dirname, './dist'), filename: '[name].[hash].js', chunkFilename:'[id].[name].[chunkhash].js' }, resolve: { extensions: ['.js', '.vue'], alias: { 'vue$': 'vue/dist/vue.esm.js', 'public': path.resolve(dirname, './public') } }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { objectAssign: 'Object.assign' } }, { test: /\.css$/, loader: ['style-loader', 'css-loader'] }, { test: /\.styl$/, loader: ['style-loader', 'css-loader', 'stylus-loader'] } ] }, devServer: { historyApiFallback: true, noInfo: true }, performance: { hints: false }, devtool: '#eval-source-map', plugins: [ new BundleAnalyzerPlugin(), new CleanWebpackPlugin(['dist']), generateHtml, new webpack.optimize.CommonsChunkPlugin({ name: 'ventor' }), ] } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
CommonChunkPlugin
ventor 입구 여기서는 axios와 같은 node_module 아래의 모든 참조 모듈이 필터링되지 않았으므로 이를 app.js를 패키징했는데 여기에서 분리를 수행합니다.
entry: { vendor: ['vue', 'vue-router', 'vuetify', 'axios'], app: './src/main.js' },
여기서 또 다른 문제가 있습니다. 이때는 자동으로 Ventor를 분리해야 할 수도 있습니다. minChunks 구성에서는 다음과 같이 mode_module에서 참조되는 모든 모듈의 구성을 패키징하고 수정할 수 있습니다
entry: { //vendor: ['vue', 'vue-router', 'vuetify', 'axios'], //删除 app: './src/main.js' } new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: ({ resource }) => ( resource && resource.indexOf('node_modules') >= 0 && resource.match(/\.js$/) ) }),
위 단계의 최적화 후 파일 배포를 살펴보면 node_module 아래의 모듈이 모두 아래에 수집되어 있음을 알 수 있습니다. 공급 업체.
여기서 경험을 얻을 수 있습니다. 즉, 프로젝트에서 node_module 아래의 모듈을 구체적으로 패키징하고 최적화할 수 있습니다. 그런데 여기서 조심하면 node_module에도 codemirror 컴포넌트가 있는 것을 알 수 있는데 왜 패키징되지 않고 다른 단일 페이지에 반복적으로 패키징되는 걸까요? 사실 commonChunk에서 name 속성을 사용한다는 것은 실제로 Follow만 한다는 의미이기 때문입니다. 종속 패키지를 찾기 위한 항목입니다. 구성 요소가 비동기적으로 로드되므로 여기에 패키지되지 않습니다. 이제 dbmanage 및 시스템 페이지의 라우팅 지연 로딩을 제거하고
// const dbmanage = () => import(/* webpackChunkName: "dbmanage" */'../views/dbmanage.vue') // const system = () => import(/* webpackChunkName: "system" */'../views/system.vue') import dbmanage from '../views/dbmanage.vue' import system from '../views/system.vue'
이때, 리패키징을 해보면 코드미러가 패키징되어 있는 걸 볼 수 있는데, 이게 좋은 걸까?
비동기
上面的问题答案是肯定的,不可以的,很明显ventor是我们的入口代码即首屏,我们完全没有必要去加载这个codemirror组件,我们先把刚才的路由修改恢复回去,但是这时又有了新问题,我们的codemirror被同时打包进了两个单页面,并且还有些自己封装的components,例如MTable或是MDataTable等也出现了重复打包。并且codemirror特别大,同时加载到两个单页面也会造成很大的性能问题,简单说就是,我们在访问第一个单页面加载了codemirror之后,在第二个页面其实就不应该再加载了。 要解决这个问题,这里我们可以使用 CommonsChunkPlugin 的 async 并在 minChunnks 里的count方法来判断数量,只要是 重用次数 超过两个包括两个的异步加载模块(即 import () 产生的chunk )我们都认为是 可以 打成公共的 ,这里我们增加一项配置。
new webpack.optimize.CommonsChunkPlugin({ async: 'used-twice', minChunks: (module, count) => ( count >= 2 ), })
再次打包,我们发现所有服用的组件被重新打到了 0.used-twice-app.js中了,这样各个单页面大小也有所下降,平均小了近10k左右
可是,这里我们发现vuetify.js和vuetify.css实在太庞大了,导致我们的打包的代码很大,这里,我们考虑把它提取出来,这里为了避免重复打包,需要使用external,并将vue以及vuetify的代码采用cdn读取的方式,首先修改index.html
//css引入 <link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css"> <link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="external nofollow" rel="stylesheet"> //js引入 <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vuetify/dist/vuetify.js"></script> //去掉main.js中之前对vuetifycss的引入 //import 'vuetify/dist/vuetify.css'
再修改webpack配置,新增externals
externals: { 'vue':'Vue', "vuetify":"Vuetify" }
再重新打包,可以看到vue相关的代码已经没有了,目前也只有used-twice-app.js比较大了,app.js缩小了近200kb。
但是新问题又来了,codemirror很大,而used-twice又是首屏需要的,这个打包在首屏肯定不是很好,这里我们要将system和dbmanage页面的codemirror组件改为异步加载,单独打包,修改如下:
// import MCode from "../component/MCode.vue"; //注释掉 components: { MDialog, MCode: () => import(/* webpackChunkName: "MCode" */'../component/MCode.vue') },
重新打包下,可以看到 codemirror被抽离了,首屏代码进一步得到了减少,used-twice-app.js代码缩小了近150k。
做了上面这么多的优化之后,业务测的js基本都被拆到了50kb一下(忽略map文件),算是优化成功了。
总结
可能会有朋友会问,单独分拆vue和vuetify会导致请求数增加,这里我想补充下,我们的业务现在已经切换成http2了,由于多路复用,并且加上浏览器缓存,我们分拆出的请求数其实也算是控制在合理的范畴内。
这里最后贴一下优化后的webpack配置,大家一起交流学习下哈。
const path = require('path') const webpack = require('webpack') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const generateHtml = new HtmlWebpackPlugin({ title: '逍遥系统', template: './src/index.html', minify: { removeComments: true } }) module.exports = { entry: { app: './src/main.js' }, output: { path: path.resolve(dirname, './dist'), filename: '[name].[hash].js', chunkFilename:'[id].[name].[chunkhash].js' }, resolve: { extensions: ['.js', '.vue'], alias: { 'vue$': 'vue/dist/vue.esm.js', 'public': path.resolve(dirname, './public') } }, externals: { 'vue':'Vue', "vuetify":"Vuetify" }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { objectAssign: 'Object.assign' } }, { test: /\.css$/, loader: ['style-loader', 'css-loader'] }, { test: /\.styl$/, loader: ['style-loader', 'css-loader', 'stylus-loader'] } ] }, devServer: { historyApiFallback: true, noInfo: true }, performance: { hints: false }, devtool: '#eval-source-map', plugins: [ new CleanWebpackPlugin(['dist']), generateHtml ] } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' module.exports.plugins = (module.exports.plugins || []).concat([ new BundleAnalyzerPlugin(), new webpack.optimize.CommonsChunkPlugin({ name: 'ventor', minChunks: ({ resource }) => ( resource && resource.indexOf('node_modules') >= 0 && resource.match(/\.js$/) ) }), new webpack.optimize.CommonsChunkPlugin({ async: 'used-twice', minChunks: (module, count) => ( count >= 2 ), }), new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
위 내용은 Vue 패키징 단계를 최적화하기 위한 코드 분할에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!