若是你对webpack
不是很了解,请你关注我以前的文章,都是百星以上star
的高质量文javascript
《前端进阶》
专栏 一块儿突破学习文章内容都会不按期更新 记得必定要收藏
webpack
webpack
用了会上瘾,它也是突破你技术瓶颈的好方向,如今基本上任何东西都离不开webpack
,webpack
用得好,什么next nuxt
随便上手(本人体会很深),本人参考了Vue
脚手架,京东的webpack
优化方案,以及本人的其余方面优化,着重在生产模式
下的构建速度优化提高很是明显(固然开发环境下也是~),性能提高很明显哦~.Vue
文件和template模板
tree shaking
摇树优化 删除掉无用代码async / await
和 箭头函数PWA
功能,热刷新,安装后当即接管浏览器 离线后仍让能够访问网站 还能够在手机上添加网站到桌面使用preload
预加载资源 prefetch
按需请求资源 ,这里除了dns
预解析外,建议其余的使用按需加载组件,顺便代码分割,这也是京东的优化方案nginx
,拦截非预期请求(京东的方案)CSS
模块化,不怕命名冲突base64
处理sx js json
等VueRouter
路由懒加载,按需加载 , 代码分割 指定多个路由同个chunkName
而且打包到同个chunk
中 实现代码精确分割less sass stylus
等预处理code spliting
优化首屏加载时间 不让一个文件体积过大chunkhash
,每一个文件有对应的contenthash
,方便浏览器区别缓存CSS
压缩CSS
前缀 兼容各类浏览器code spliting
CSS
文件单独抽取出来每隔三天,技术就会进步一次
// 入口文件 entry: { app: './src/js/index.js', }, // 输出文件 output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), publicPath: '/' //确保文件资源可以在 http://localhost:3000 下正确访问 }, // 开发者工具 source-map devtool: 'inline-source-map', // 建立开发者服务器 devServer: { contentBase: './dist', hot: true // 热更新 }, plugins: [ // 删除dist目录 new CleanWebpackPlugin(['dist']), // 从新穿件html文件 new HtmlWebpackPlugin({ title: 'Output Management' }), // 以便更容易查看要修补(patch)的依赖 new webpack.NamedModulesPlugin(), // 热更新模块 new webpack.HotModuleReplacementPlugin() ], // 环境 mode: "development", // loader配置 module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] }
这里面咱们重点关注 module
和plugins
属性,由于今天的重点是编写loader
和plugin
,须要配置这两个属性。css
webpack
启动后,在读取配置的过程当中会先执行 new MyPlugin(options)
初始化一个 MyPlugin
得到其实例。在初始化 compiler
对象后,再调用 myPlugin.apply(compiler)
给插件实例传入 compiler
对象。插件实例在获取到 compiler
对象后,就能够经过 compiler.plugin
(事件名称, 回调函数) 监听到 Webpack
广播出来的事件。
而且能够经过 compiler 对象去操做 webpack。html
Compiler
对象包含了 Webpack
环境全部的的配置信息,包含 options,loaders,plugins
这些信息,这个对象在 Webpack
启动时候被实例化,它是全局惟一的,能够简单地把它理解为 Webpack
实例;前端
Compilation
对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的
Compilation 将被建立。
Compilation 对象也提供了不少事件回调供插件作扩展。经过
Compilation 也能读取到
Compiler` 对象。Compiler 和 Compilation
的区别在于:Compiler
表明了整个 Webpack
从启动到关闭的生命周期,而 Compilation
只是表明了一次新的编译。webpack
经过 Tapable
来组织这条复杂的生产线。webpack
的事件流机制保证了插件的有序性,使得整个系统扩展性很好。webpack
的事件流机制应用了观察者模式,和 Node.js 中的 EventEmitter
很是类似。Commonjs、amd
或者es6
的import,webpack
都会对其进行分析。来获取代码的依赖)webpack
作的就是分析代码。转换代码,编译代码,输出代码webpack
的一些基础知识,对于理解webpack的工做机制颇有帮助。commonjs
模块化方案,若是你不是很懂,那么看起来很费劲,我写的脚手架,就不使用模块化方案了,简单粗
暴yarn
不解释 就用yarn
webpack.dev.js
开发模式下的配置yarn init -y
yarn add webpack webpack-cli
(yarn
会自动添加依赖是线上依赖仍是开发环境的依赖)entry: path.resolve(__dirname, '../src/main.js')}
output: { filename: 'js/[name].[hash:5].js', path: path.resolve(__dirname, '../dist'), },
Vue
脚手架里基本配置的loader
,后面的loader
都是往rules
数组里加就好了~module: { rules: [ { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use: [{ loader: 'url-loader', options: { limit: 10000, name: 'img/[name]-[hash:5].[ext]', } } ] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: 'fonts/[name]-[hash:5].[ext]', } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [ { loader: 'url-loader', options: { limit: 4096, name: 'media/[name]-[hash:5].[ext]', } } ] } ] },
有人会问 这么多我怎么看啊 别急 第一个url-loader
是处理base64
图片的,让低于limit
大小的文件以base64
形式使用,后面两个同样的套路,只是换了文件类型而已 ,不会的话,先复制过去跑一把?
.vue
文件和tempalte
模板 , yarn add vue vue-loader vue-template-compiler
加入loader { test:/\.vue$/, loader:"vue-loader" } 加入plugin const vueplugin = require('vue-loader/lib/plugin') 在webpack的plugin中 new vueplugin()便可
babel-polifill
,vendor
代码分割公共模块,打包后这些代码都会在一个公共模块app: ['babel-polyfill', './src/index.js', './src/pages/home/index.js', './src/pages/home/categorys/index.jsx'], vendor: ['vuex', 'better-scroll', 'mint-ui', 'element-ui']
html
文件为模板打包输出,自动引入打包后的js
文件const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname,'../index.html'), filename: 'index.html' }), ]
.vue
的后缀 ,直接配置在module.exports
对象中,跟entry
同级resolve: { extensions: ['.js','.json','.vue'], }
html
文件的loader
{ test: /\.(html)$/, loader: 'html-loader' }
const os = require('os') { loader: 'thread-loader', options: { workers: os.cpus().length } }
babel-loader
加入 babel-loader 还有 解析JSX ES6语法的 babel preset@babel/preset-env解析es6语法 @babel/plugin-syntax-dynamic-import解析vue的 import按需加载,附带code spliting功能 { test: /\.(js|jsx)$/, use: { loader: 'babel-loader', options: { presets: ["@babel/preset-env", { "modules": false }] ,//附带`tree shaking` plugins: ["@babel/plugin-syntax-dynamic-import"] }, cacheDirectory: true//开启babel编译缓存 } },
babel
配置后 咱们躺着就能够用vueRouter
的路由懒加载了第二,在 Webpack 中,咱们可使用动态 import语法来定义代码分块点 (split point):vue
import('./Foo.vue') // 返回 Promise
注意
const Foo = () => import('./Foo.vue') 在路由配置中什么都不须要改变,只须要像往常同样使用 Foo: const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] }) # 把组件按组分块 有时候咱们想把某个路由下的全部组件都打包在同个异步块 (chunk) 中。只须要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (须要 Webpack > 2.4)。 const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
const HtmlWebpackPlugin = require('html-webpack-plugin') const webpack = require('webpack') new HtmlWebpackPlugin({ template: './src/index.html' }), new webpack.HotModuleReplacementPlugin(), devServer: { contentBase: '../build', open: true, port: 5000, hot: true },
less-css
识别的模块{ test: /\.(less|css)$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' , options: { modules: false, //不建议开启css模块化,某些ui组件库可能会按需加载失败 localIdentName: '[local]--[hash:base64:5]' } }, { loader: 'less-loader', options: { javascriptEnabled: true } } ] },
下面正式开始生产环境
html
杀掉无效的代码new HtmlWebpackPlugin({ template: './src/index.html', minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, } }),
{ test: /\.(jpg|jpeg|bmp|svg|png|webp|gif)$/, use:[ {loader: 'url-loader', options: { limit: 8 * 1024, name: '[name].[hash:8].[ext]', outputPath:'/img' }}, { loader: 'img-loader', options: { plugins: [ require('imagemin-gifsicle')({ interlaced: false }), require('imagemin-mozjpeg')({ progressive: true, arithmetic: false }), require('imagemin-pngquant')({ floyd: 0.5, speed: 2 }), require('imagemin-svgo')({ plugins: [ { removeTitle: true }, { convertPathData: false } ] }) ] } } ] }
{ exclude: /\.(js|json|less|css|jsx)$/, loader: 'file-loader', options: { outputPath: 'media/', name: '[name].[contenthash:8].[ext]' } }
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') new OptimizeCssAssetsWebpackPlugin({ cssProcessPluginOptions:{ preset:['default',{discardComments: {removeAll:true} }] } }),
vue
脚手架是同步异步分开割,我是直接一块儿割optimization: { runtimeChunk:true, //设置为 true, 一个chunk打包后就是一个文件,一个chunk对应`一些js css 图片`等 splitChunks: { chunks: 'all' // 默认 entry 的 chunk 不会被拆分, 配置成 all, 就能够了拆分了,一个入口`JS`, //打包后就生成一个单独的文件 } }
pwa这个技术其实要想真正用好,仍是须要下点功夫,它有它的生命周期,以及它在浏览器中热更新带来的反作用等,须要认真研究。能够参考百度的lavas框架发展历史~ const WorkboxPlugin = require('workbox-webpack-plugin') new WorkboxPlugin.GenerateSW({ clientsClaim: true, //让浏览器当即servece worker被接管 skipWaiting: true, // 更新sw文件后,当即插队到最前面 importWorkboxFrom: 'local', include: [/\.js$/, /\.css$/, /\.html$/,/\.jpg/,/\.jpeg/,/\.svg/,/\.webp/,/\.png/], }),
VUE
首选nuxt
框架,也可使用它的脚手架next nuxt
和pwa
的使用~