编译es6:
基础:babel-loader --save-dev babel-core --save-dev;要配置babel以什么规范打包(针对语法)要用到babel-preset-env --save-dev,如es2015/es2016/es2017/env(2015-2017及最近),rules.use.options.presets:['env',{targets:{browsers: ['> 1%', 'last 2 versions']}}]。
babel-polyfill --save(在代码开始就要import进来)和babel-runtime --save、babel-plugin-transform-runtime --save-dev,(es的函数方法一些低版本浏览器还不具有,需要用到polyfill和runtime这两个babel插件)。(比如Generator Set Map Array.form Array.prototype.include都不会被babel处理),babel-polyfill为全局垫片(全局可以使用新函数方法)为应用准备(为自己所用),babel-runtime为局部垫片为框架(开发给别人用)准备。我们可以在根目录下.babelrc中配置babel。
提取公共代码:
主要针对多页面应用,减少代码冗余,提高加载速度(spa公共代码提前加载出来,非spa公共代码缓存起来)。
CommonsChunkPlugin
webpack.optimize.CommonsChunkPlugin,webpack内置插件,其中async就是指定了对异步公共模块中依赖的相同模块,把它们提取出来的chunkname。(比如pageA异步加载了模块subPageA,subPageB,而subPageA,subPageB都依赖module,那么就是把module提取出来。)场景:单页应用、单页应用+第三发依赖、多页应用+第三发依赖+webpack生成代码
单页应用,commonsChunk是针对entry中多个入口文件(多页面应用)进行公共代码打包的,单入口(单页面应用)是无效的。
多页面应用,a1和a2都依赖b。在entry中把a1和a2都作为入口,就能把b单独打包出来成公共模块。
多页面应用+第三方依赖,
多页面应用+第三方依赖new webpack.optimize.SplitChunksPlugin({}) 或者optimization.splitChunk{()}
每个页面都要的公共代码提取出来后直接以script形式插入代码中而不进行http请求,html-webpack-inline-chunk-plugin,new HtmlInlinkChunkPlugin({ inlineChunks(指定要插入代码的模块名): ['mainifest'] })
代码分割和懒加载:
使用场景:分离业务代码和第三方依赖、分离业务代码和业务公共代码和第三方依赖、分离首次加载和访问后加载的代码。
实现方式一,webpack内置方法。
require.ensure(对原生promise有依赖,需要引入babel-polyfill)动态加载一个模块,参数:[],dependencies(依赖,加载进来并不会执行,其callback内还要通过require执行代码。当不指定依赖时,只在callback内require会变成异步);callback,回调函数里才会执行代码;chunkName(打包生产的模块名)。
require.include是针对两个子模块都引用了相同模块,当两个自模块都依赖了一个第三方模块,可以提前把第三个模块放到父模块中(比如page依赖pageA和pageB,而pageA/pageB依赖module,那么module不会被打包进pageA/pageB,直接打包进page)。
例子实现方式二,ES2015 Loader spec。
important替代require.ensure,important进来就已经执行,这点和require.ensure不同。important(/*webpackChunkName: async-chunk-name*/ /*webpackMode: lazy*/ modulename).then(...)(需要引入babel-polyfill),既符合es6语法,又实现代码分割懒加载功能。若两个important写了相同的webpackChunkName则打包文件会合并到这个chunk里。
注意:使用import报错的话。npm install --save-dev babel-plugin-syntax-dynamic-import。.babelrc 中加上插件"plugins": ["syntax-dynamic-import"]。
例子1 例子2处理css:
如何通过loader引入css文件?
如何做到css模块化,不再是全局的?
如何通过loader去处理sass和less?
如何提取css代码?把css提取出一个单独文件,他会有自己的缓存,用到这个样式的多个文件,在第一个文件就已经缓存住了,接下来文件速度更快。
如何提取公共css代码?
1、style-loader是用来创建style标签,把提取出来的css放入html的style标签中,options: insertAt(插入位置)、insertInto(插入到dom,如:'#app')、singleton(是否只使用一个style标签,true就进行合并到一个style中,因为像ie有style个数标签限制)、transform(路径.js,浏览器环境下在把css插入html前运行,比如在此时我们可以根据浏览器不同类型做不同操作,会在每一个css文件插入前都执行一遍)。
另外:style-loader/url(在页面中创建link标签,一般不用)。style-loader/useable(import a from 'xx.css'后,可以通过在js中写a.use(),a.unues()来改变这段css引入或去掉)。
2、css-loader是解决在js中可以import css 进来。options:alias(解析的别名,当你匹配到的路径下的css里面你要import另外一个css时,alias在import的路径中可以生效)。importLoader(@import,是否生效,取决于css-loader后面是否还有其他loader)。Minimize(boolean,是否压缩css)。modules(boolean,是否启用css模块化)。
css模块化的语法,':local':局部样式;':global':全局样式;'compose':继承样式;'compose....from path':从某个文件引入样式。
3、配置Sass/Less。Less: npm install less-loader less --save-dev。Sass: npm install sass-loader node-sass --save-dev。
sass-loader、 node-sass,css预编译. 执行顺序是 sass-loader 》postcss-loader 》css-loader 》 style-loader,loaders是从下往上执行的,
4、提取css。extract-text-webpack-plugin 或者 extract-loader,我们用前者。1、匹配sass时用ExtractTextPlugin.extract({fallbak(提取出来之后):{loader:'style-loader'},use:[提取之前处理 ..]}) 。2、plugins中new ExtractTextWebpackPlugin({ filename:(提取出的css叫什么[name].min.css) ,allChunks: false (异步加载的JS模块中引入的css/sass/lss最终都会被打包进js模块中,并且能参与公共代码分割等)})。
5、postCss。postcss、postcss-loader、autoprefixer、cssnano(优化压缩css,css-loader已经集成了这个功能,minmize:true即可)、postcss-cssnext(可以使用css新语法如css 变量(variable), 自定义选择器(custom selectors),动态计算calc() )。autoprefixer使用:{loader: 'postcss-loader', options: {ident: 'postcss'(表明接下来插件是给postcss用的), plugins: [ require('autoprefixer')() ]}}
另外,postcss-import(帮助我们把通过@import引入的文件内容取到我们的css中,取过来时相对路径会变化,要配合postcss-url帮忙转换。另外还有postcss-assets。)
想要所有插件都共用一份浏览器适配配置,1、可以在package.json中配置。2、可以在项目根目录建一个.browserslistrc文件进行配置。
例子Tree Shaking:
对用不到的代码,打包时进行删除。使用场景:1、常规优化自己代码。2、引入第三方库代码。包括 js tree shaking和 css tree shaking。
js tree shaking: webpack打包时会把没有用到的方法标识出来,通过插件 uglifyjs-webpack-plugin 或者 webpack.optimize.uglifyJs(不支持es6)来删除它们。但有些第三方库比如Lodash不是以export方式输出代码块的,我们不能进行删减,我们需要借助一些插件,比如lodash需要下载一个 babel-plugin-lodash ,在babel-loader的options中配置{present: ['env'], plugins: ['lodash']。
问题:Babel默认将ES6模块通过commonJs模块转换输出,此时利用ES6模块的tree-shaking就不灵了。
方法:babel-preset-env有个modules的配置项就是控制这个的,把它设置成false就不会把ES6模块转换成commonJs了。
把modules设置为false 实例css tree shaking:需要用到 purifycss-webpack glob-all(其中有glob.sync可以用来多路径同时加载)。该插件必须放在css提取插件extractTextWebpackPlugin插件后面。new PurifyCss({ path: glob.sync([ path.join(_dirname, 'app/html/*.html'), path.join(_dirname, 'app/js/*.js') ]) })
文件处理:
1、如何引入图片,引入图片后webpack如何处理图片。2、合成雪碧图。2、压缩图片。3、小图片不需要压缩,进行base64编码。
url-loader/file-loader +img-loader +postcss-sprites
处理图片:file-loader(我们css中写的路径是相对于css的,而打包后文件路径是相对于入口html文件的,file-loader主要处理文件路径问题) options: {publicPath(处理过图片路径前一起加一段路径):'',outputPath(图片打包输出文件路径):' ',useRelativePath(打包出文件路径是否使用相对与此配置文件的相对路径): true,name(打包后输出文件名name): '[name].min.[ext]'}
图片base64编码:对小图片进行base64编码减少http请求:url-loader(和file-loader功能差不多就多了个base64编码,打包出的图片文件名可以在url-loader/file-loader中配置name:[name]-[hsah:5].[ext]),options:{ limit(对小于这个值大小图片进行base64编码): 10000,其他同file-loader一样,可以替代file-loader }
压缩图片:img-loader ,options: {pngquant: { quality(图片质量控制): 80 }}
合成雪碧图:postcss-sprites 在postcss-loader中 options: { ident: 'post', plugins: [ require('postcss-sprites')( { spritePath(雪碧图输出路径): ' dist/.. ' ,retina: true} ) ] }。设计师提供2倍大小图片高清适配,那么2倍大小图片如何做雪碧图?1、设置retina:true,2、告诉postcss-sprites哪些屏幕是retina图片,即在图片名后面加上@2x
处理字体文件:先把字体文件下载放到项目中,css中@font-face{font-family(自定义字体名): '..' src: url('字体文件路径') }注册, 最后在css中使用 它就好了。url-loader
处理第三方库:webpack.ProvidePlugin 、imports-loader 、 window 命名空间。需要在每个模块中都引入一段代码,1、若远程cdn上的第三方库,直接在页面<script src="..">(针对单页面入口文件,多页面还得每个页面添加)。2、代码在自己项目目录下,webpack.ProvidePlugin或者imports-loader。若npm下载的第三方库,new webpack.ProvidePlugin({ $(自定义可以使用的变量名): 'jquery'(模块的名称) }) 或者 { test: path.resolve(_dirname, 'src/app.js'), use: [ { loader: 'imports-loader', options: { $: jquery } } ] }。 若不是npm下载,是自己项目目录下的js,需要先配置别名,resolve: { alias: { jquery$(这里的$符为确定接下来路径是准确文件,不是目录) : path.resolve(_dirname, 'src/libs/jquery.min.js') } },接下来同上。
生成html:
生成html并在html中注入css和js。HtmWebpackPlugin,new HTMLWebpackPlugin({ filename(生成的 HTML 文件名): '..', template(生成 HTML 文件使用的模板): path.resolve(__dirname, '...'), chunks(要注入的js模块,默认全注入):['..'],minify(是否压缩生成的html: collapseWhitespace: true(进行压缩)),inject(是否把js,css插入你生成的html文件中): 默认true})
html中引入图片、路径:两种方法。1、html-loader, { option: { attrs: [ 'img:src', 'img(html标签): data-src(标签属性)' ] } } 。有一个问题,如果我们css中图片路径是用(../a.png)相对路径(相对打包出的css文件),html中也用(../b.png)相对路径(相对打包出的html文件),url-loader中处理时uesRelativePath: true,这样 打包生成的img图片会出现在两个位置。我们这样的话,只能把路径改成绝对路径(url-loader中处理时outputPath: 'assets/imgs/',把output中 publicPath: '/'),因为url-loader不能分别定义相对路径,我们css中url和html中的url相对的不是同一个地方。2、直接在html中img src="${require('src/assets...')}"
搭建本地开发环境:
1、webpack watch mode (输入命令webpack -watch),webpack会去监听文件变化实时打包,但并不会起一个web服务器。
2、webpack-dev-server,clean-webpack-plugin(打包之前先删除之前打包的文件夹,new CleanWebpackPlugin(path.resolve(__dirname, '../dist')指定要清除目录,和根路径(得在清除目录的上级));devServer{ inline(true:在控制台显示打包情况,否则在浏览器顶部显示), port(端口号),historyApiFallback(主要对单页应应用,跳页时候不会刷新浏览器),contentBase(提供内容的路径,默认为当前工作目录),https(指定生成证书),proxy(集成了http-proxy-middleware指定远程接口代理),hot(模块热更新),lazy(刚启动服务器时不打包任何东西,打开相应页面加载打包相应模块)},overlay(在页面中打开一个遮罩显示错误提示)
Proxy: { '/api'(匹配请求中以‘/ap’i开头的路径) : { target(指定请求路径): 'https://..',changeOrigin(一般为true): true,pathRewrite(把请求中/a替换为/a/b,请求时少写点代码): { '^/a' : '/a/b'},headers(设置请求头): {'cookie': '...'} }}
hot(模块热更新):当我们修改代码时,1、应用之前请求的数据状态都能保存下来,2、不用刷新页面可以节省调试时间,3、样式调试更快。可以通过devServer.hot来开启模块热更新。webpack.HotModuleReplacementPlugin + webpack.NamedModulesPlugin。css模块热更新会由style-loader进行处理,我们只需开启hot即可。js模块热更新我们需要手动写一些代码,需要热更新时候我们会用到API:module.hot.accept接受两个参数,1、依赖。2、依赖更新后回调函数,回调里需要对你需要更新的依赖进行重载(先移除,再加载进来)。
3、express + webpack-dev-middleware(比2更加灵活自由,需要自己写的配置项更多)
express/koa(第三方node服务) webpack-dev-middleware(中间件) webpack-hot-middle(模块热更新) http-proxy-middle(服务器代理) connect-history-api-fallbak(地址reback) opn(打开浏览器)
设置Eslint检查代码格式:
eslint eslint-loader eslint-plugin-html(对html中script标签中代码进行检查) eslint-friendly-formatter(报错报告格式) eslint-plugin-import , eslint-config-standard eslint-plugin-node eslint-plugin-promise eslint-plugin-standard(遵从Javascript Standard Style规则来验证我们需要的代码 所需要下载的配置,或者去github上搜eslint-config-xxx开头的eslint配置) 。 给js加一个eslint-loader,得在babel-loader之前去处理他,也就是写在babel-loader之后,options: { formatter: require('eslint-friendly-formatter') },可以在根目录创建.eslintrc.js进行eslint配置 { root(是否在根目录): true,extends(标准版): 'standard',plugins: [ ' html' ], env(在哪些环境开启检查): { browser: true, node: true },rules:[...] } 。 在devServer中开启overlay: true,可以在浏览器页面直接看到错误。
代码调试sourceMap
devtool(sourceMap): 源代码和打包后代码不同,我们要调试代码的话,需要用到它。css使用sourceMap 出了在dev-server中开启它,css-loader sass-loader中也要开启sourceMap,js中UglifyJsPlugin的sourceMap也要打开。开发环境推荐 cheap-module-source-map,生产环境的话直接source-map。
长缓存优化:
用户请求网页数据时,服务器返回请求时可以通过控制http协议的响应头告诉浏览器某些资源是在某一段时间内是长缓存不用更新,这些资源有自己版本号,当这些版本号不发生改变,浏览器不会去发请求或者发请求浏览器也会从本地加载资源,可以减少用户访问网页的时间。
场景:改变app代码,vendor变化。
1、我们需要单独提取vendor进行打包。把vendor有一个独立的enter就行。
2、单独打包出webpack runtime(打包时候webpack自己会产生的代码,每个打包文件都会有,内容每次打包都不一样,会影响chunkhash值变化)。
3、控制版本号的hash(每一次打包的hash,内容相同,两次打包hash值不同)值改为chunkhash(代码的hash,内容相同,两次打包chunkhash值相同),内容不发生变化时,让打出的版本号也不变。
场景:当引入新模块,模块顺序变化, vendor chunkhash也会变化(原因:webpack在打包时候会给每个chunk一个id,id发生变化也会导致chunkhash发生变化)。这时候我们需要用到两个插件
1、new webpack.NamedChunksPlugin() 把打包文件的chunksId由原来的序号(会变)转为文件名(不变)。new webpack.NamedModulesPlugin() 把打包文件里的模块的id也由原来的序号(会变)转为文件名(不变),因为它也会影响chunkhash。
场景:若模块是动态引入的,他的chunksId由还会是的序号(会变),高版本webpack已经解决这个问题。低版本可以异步important时候/* webpackChunkName: '..' */ 指定webpackChunkName就可以了。
开发环境和生产环境区分。
不同点:开发环境,1、模块热更新。2、sourceMap调试代码。3、接口代理。4、代码规范检查。生产环境,1、提取公用代码。2、压缩混淆。3、文件压缩或者base64编码。4、去除无用的代码。共同点:1、同样的入口。2、同样的代码处理(loader处理)、3、同样的解析配置。
打包分析、优化
优化打包速度:
1、分开第三方代码(vendor)和业务代码(app)。使用DllPlugin插件和DllReferencePlugin插件将两者分开打包。
2、UglifyJsPlugin压缩混淆是非常耗时的工作,parallel参数(串连处理->并行处理):true。cache参数开启
3、HappyPack针对loader,串行处理->并行处理。
4、babel-loader打包耗时,options.cacheDirectory(开启缓存)。include,exclude(限定范围)
5、减少resolve,Devtool:去除sourcemap,cache-loader把所有loader处理的结果缓存下来。升级node和webpack。
Webpack和Vue-cli:
初始化项目:vue init <template name>(一般webpack) <project name>
项目结构:
static:文件夹下文件不会进行解析,打包直接移过去。一般用来放没有被npm管理起来的第三方包。
src/assets:业务相关图片字体。
test:测试用例相关。
基本命令:
开发配置:
工具配置:
几种不同hash:
hash:跟整个webpack构建项目相关的,每次项目构建hash对应的值都是不同的,即使项目文件没有做“任何修改”(其实是有修改的,因为每次webpack打包编译都会注入webpack的运行时代码,导致整个项目有变化,所以每次hash值都会变化的。).该方式是无法达到缓存的,因为每次hash都会变化
chunkhash:它是跟webpack打包的chunk相关的,具体来说webpack是根据入口entry配置文件来分析其依赖项并由此来构建该entry的chunk,并生成对应的hash值。不同的chunk会有不同的hash值。一般在项目中把公共的依赖库进行单独打包构建,用chunkhash来生成hash值,只要依赖公共库不变,那么其对应的chunkhash就不会变,从而达到缓存的目的。
contenthash:contenthash表示由文件内容产生的hash值,内容不同产生的contenthash值也不一样。在项目中,通常做法是把项目中css都抽离出对应的css文件来加以引用。比方在上面a.html中依赖一个index.css文件,index.css的hash是跟着a.html的chunkhash走的,只要a.html文件变更的话,那么即使index.css文件没有变化,它的hash值也是会跟着变化的,导致缓存失效,那么这时我们可以使用extra-text-webpack-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,它的hash值就不会变。
file-loader的hash: 对于图片、字体等静态资源,在使用webpack构建提取时,其实是使用了file-loader来完成的,生成对应的文件hash值也就是由对应的file-loader来计算的。那么这些静态文件的hash值不是webpack每次项目构建的hash,它是由file-loader根据文件内容计算出来的,不要误认为是webpack构建的hash。
打包结果分析:
官方:Offical Analyse Tool:
Mac :webpack --profile --json > stats.json
Windows :webapck --profile --json | Out-file 'state.json' - Encoding OEM
在state.json会有打包的信息,带着json文件去 http://webpack.github.io/analyse/
社区:webpack-bundle-analyzer:
使用方式:
插件:BundleAnalyzerPlugin
命令行:webpack-bundle-analyzer state.json
vue-cli2脚手架优化
1、图片相关
生产环境图片压缩。
图片压缩合成雪碧图:我们一般按模块进行开发,为了方便管理,模块相关非共用资源独立和模块放在一起。
各个模块,需要合并成1张图的图片资源放在sprites中,其他图片放在imgs中 公共资源文件,需要合成的雪碧图和非雪碧图也分开放出现情况分析:
1、模块a中同时引用自己的雪碧图和公共的雪碧图,打包会把 两者合成1张图。
2、模块a和b中同时引用自己的雪碧图和公共的雪碧图,三者打包会 合成1张图。
2、css全局样式相关
我们可能需要引入全局样式(比如sass的mixin,variable)。
如果你想在main.js中import xx.scss,那是无效的,因为vue-loader的loader不支持对.scss文件的处理(你可以自己去配loader)。
你可以在每个vue模块中去@import xxx.scss,如果是全局样式,那太繁琐。
更方便的方法是 使用sass-resources-loader插件,来实现sass全局变量及方法注入。
3、TreeShaking相关
js-shaking是ok的,我们主要针对.vue文件进行css treeshaking
我想对.vue文件进行css treeshaking,我用了purgecss-webpack-plugin或者purifycss-webpack插件,他们对于指定的js文件或者html文件是可以正常进行 treeshaking的,但是对于指定的.vue文件,并不起效。
css treeshaking4、代码分割和长缓存优化相关
webpack-runtime提取和vendor(凡是引用了node_modules中文件的资源都被归为vendor)提取和异步代码块中的公共代码提取(require.include和CommonsChunkPlugin的async都可以做到),vue-cli2都帮我们做到了。
单入口同步公共代码块貌似现在不支持单独提取出来打包。
我们可以优化的点:生产环境使用new webpack.NamedChunksPlugin()插件,把打包文件的chunksId由原来的序号(当有新独立文件产生,同时会影响其他文件chunkId)转为文件名(不变),因为id会影响chunkName,为了保持其他文件的长缓存。另外动态独立打包的文件和第三方依赖的chankName,vue-cli2都给我们指定好了。
网友评论