[toc]

1. devServer

这里不晓得你有没有发现后面几节课调试的时候一个比拟繁琐的步骤,就是每一次测试打包就须要从新执行npm run bundle命令,而后手动关上dist目录上面的index.html文件看成果,咱们能不能在src目录下源代码发生变化的时候主动实现上述过程?

1.1 应用--watch参数

第一种办法咱们能够给webpack命令加上--watch参数:

// package.json"scipts":{    "watch":"webpack --watch"}

他就会监听打包源代码的变动,一旦源代码有变动他就会从新打包。从而更新dist目录上面的文件。

这是第一种解决办法。不会帮咱们起服务器,没方法做ajax调试,必须手动刷新浏览器。

1.2 webpackDevServer

上述办法只能实现主动打包,然而你想要主动关上文件,并且模仿一些服务器的个性的话是不够的,这里就须要借助devServer这个工具,这是一个webpack起的本地服务器,能够帮忙咱们监听源代码变动同时主动刷新浏览器。

根底应用

首先须要装置一下:

npm install webpack-dev-server -D

接着配置一下:

// webpack.config.jsdevServer:{    contentBase:'./dist' //服务器根门路}

而后在package.json文件加一个命令:

"start":"webpack-dev-server"

接着执行start命令,log提醒咱们在本地8080端口启动了一个服务,能够拜访,并且此时咱们扭转了源代码,他还会帮忙咱们主动刷新浏览器,所以能够很好的进步开发效率。

当初咱们发现没有dist目录了,其实devServer自身还是会对src进行打包,然而他不会放到dist目录上面而是打包放到了内存外面,所以打包速度会变快,不必放心。

同时这种以服务器模式相比之前间接关上本地index.html的还有一个益处就是能够发ajax申请,因为申请要求必须是在一台服务器上以http协定进行,而本地关上是file协定,必定不行!而开了服务器就是从本地8080端口收回,个别不会有问题----大多数前端框架就是这样应用的。

其余配置

  • open:这个配置容许你执行命令当前主动关上浏览器,主动展现界面。
  • proxy:个别前端框架有时候会配置这个参数来进行接口代理,进行跨域申请
devServer:{    ...    proxy:{        '/api':'http://localhost:3000'    }}

上述填写之后如果用户拜访8080的api端口会转发到本地3000端口。

  • port:配置devServer启动的端口

1.3 本人实现一个相似的服务

这里咱们基于node来本人编写一个简略的服务,来模仿devserver的上述性能。

首先咱们新增一个命令与对应的JS文件:

"server":"node server.js"

上面须要实现这个server.js来帮忙咱们创立这样一个性能靠近devServer的服务器

搭建环境

首先须要装置express或者koa来帮忙你疾速搭建服务器,同时你还须要监听webpack打包的变动,所以你还须要一个中间件:

npm install express webpack-dev-middleware -D

而后咱们给webpack配置文件的output字段加一个publicPath参数,使得所有打包援用的字段都会加一个/根门路:

output:{    publicPath:'/',    ...}

编写server.js

const express = require('express')const webpack = require('webpack')const app = express() // 创立服务器实例// 监听端口app.listen(3000,() => {    console.log('server is running)})

接着咱们编写一下webpack相干逻辑,这里须要借助webpack和webpackDevMiddleware的一些API:

const express = require('express')const webpack = require('webpack')const webpackDevMiddleware = require('webpack-dev-middleware')const config = require('./webpack.config.js'); //引入配置文件const complier = webpack(config); //应用webpack配合config来随时进行代码编译。返回一个编译const app = express() // 创立服务器实例// 应用complier// 中间件帮忙监听打包源代码是否有变动// 只有文件产生扭转了,complier就会从新运行,对应打包输入内容就是publicPathapp.use(webpackDevMiddleware(complier),{    publicPath:config.output.publicPath})// 监听端口app.listen(3000,() => {    console.log('server is running)})

当初你让complier编译器从新执行一次,他就会从新打包一次代码~

而后咱们改一下源代码,能够看到控制台会输入打包信息。

然而这里就是浏览器必须手动刷新,而且想要跟devServer齐全一样须要配置很多货色。这里就不持续扩大了,感兴趣的能够本人网上搜寻学习材料。

同时这里你也理解了其实node中也能够应用webpack的,能够实现很多自定义的webpack扩大服务~

2. Hot Module Replacement - 热模块更新

这里咱们来看一个问题,当你针对源代码的款式进行批改时,devServer会帮忙咱们刷新浏览器,然而此时你对HTML进行的更新都没有了(例如给页面append一个元素),必须从新进行一遍操作。

这里就能够通过HMR,批改CSS后间接更新了页面的style就行了,不须要刷新。

2.1 HMR应用

CSS

devServer:{    ...    hot:true,  //关上hmr    hotOnly:true //即使hmr没失效,我也不让浏览器主动刷新,不会做其余解决}

这样devServer配置实现了,此外还须要引入一个webpack的插件:

// webpack.config.jsconst webpack = require('webpack')...plugins:[    ...    new webpack.hotModuleRepalcemnetPlugin()]

通过上述配置,HMR性能就开启了。咱们须要重启一下devServer让配置文件失效。

咱们发现CSS批改了,并不会影响界面,只会替换CSS内容,不会改JS渲染出的内容!,大大不便了款式的调试。

JS

对于JS文件同样也会有上述CSS的问题:每一次批改某个JS文件都会发送一个http申请,申请8080,从新刷新,其余JS的状态都没有被保留下来。

咱们心愿独立模块的数据扭转不要影响其余模块的内容,这时候能够借助HMR来实现。

这里咱们把之前HMR相干配置关上,此外还须要一些额定的代码,能力失效:

//index.js...counter()number()if(module.hot){    module.hot.accpet('./number',() => {        document.body.removeChild(document.getElementById('number'))        number();    })  //如果number文件产生了变动,就会执行前面函数--number从新执行}

2.2 注意事项

这个JS的HMR不像CSS一样能够间接更新,CSS其实也要写相似的逻辑,然而在css-loader外面曾经帮你解决好了。

在vue等框架外面,其实咱们没有写过相似下面的JS HMR代码,这是因为vue-loader外面也内置了这些解决。
React外部则是借助了Babel process内置了HMR的实现。

然而要留神,如果你心愿热更新某些比拟偏门的文件,例如数据文件,默认的解决逻辑可能没有蕴含,就须要手动写相似下面的代码,没方法借助loader或者Bable Process了。

感兴趣的也能够网上持续学习HMR的底层原理

3. Babel

这里咱们解说一下如何联合Babel和Webpack来解决咱们的ES6语法

3.1 问题引入

首先咱们新建一个index.js文件,其中应用一些ES6语法:

const arr = [    new Promise(() => {}),    new Promise(() => {})];arr.map(item=>{    console.log(item)})

上述index文件是包含ES6语法的,咱们尝试应用webpack打包:

npx webpack

这里没有用devServer打包,因为这个打包后的文件不会生成到目录外面,而是保留到内存中,所以你看不到的。用webpack间接打包就行

咱们来看一下打包生成的内容,简直是一成不变把index外面内容引进来。

而之前的代码能不能运行呢,咱们关上chrome看一下(用devServer启动)。

后果咱们发现如同失常,没有问题,这是因为chrome浏览器的较新版本本身实现了对ES6的语法解析性能,然而如果咱们应用低版本或者老的IE浏览器就有可能报错,最终的起因是因为程序的兼容性较差。

咱们心愿webpack打包之后能把ES6转换成ES5的语法,这样所有浏览器都能够运行了。

3.2 Babel的介绍和应用

Babel是一种罕用的JS编译器,Babel能够把ES6语法转换为ES5语法。

Babel同webpack的配合应用,能够参考官网,有同webpack的配合教程。

这里咱们须要装置一个babel-loader以及babel的外围库@babel/core,有这个库才能够实现JS代码 -> AST -> ES5代码的过程

npm install --save-dev babel-loader @babel/core

接着依照上述阐明在config的rules外面加一条规定:

rules:[{    test:/\.js$/,    exclude:/node_modules/,    loader:"babel-loader"}]

exclude代表如果你的js文件在node-moudles就不应用babel-loader,因为这是第三方的代码,根本都做过这个本义解决了。

接着你还须要装置@babel/preset来开启ES5的转换性能:

npm install @babel/preset-env --save-dev

这是因为babel-loader这是webpack和babel的桥梁,并不会把es6翻译为es5,还须要借助其余模块,这也就是preset-env的性能。

rules:[{    test:/\.js$/,    exclude:/node_modules/,    loader:"babel-loader",    options:{        presets:["@babel/preset-env"]    }}]

而后咱们再打个包,看一下main.js打包后文件发现箭头函数变成了一般函数,let变成var.

此外你还能够配置target,来阐明哪些浏览器须要进行本义,只有配置的浏览器版本低于你指定的版本号才会应用babel进行转换解决,个别这个都会配的,因为浏览器版本高的其实针对ES6的兼容都是有的。

rules:[{    test:/\.js$/,    exclude:/node_modules/,    loader:"babel-loader",    options:{        presets:[["@babel/preset-env",{            targets:{                edge:"17",                chrome:"67",                firefox:"60",                safari:"11.1"            }        }]]    }}]

3.3 babel/polyfill应用

然而仅仅做这种转换够吗?还不够,例如数组的map办法其实在低版本的浏览器还是不存在的,所以这里不仅仅须要preset-env做语法的转换,还须要想方法把缺失的变量或函数补充到代码中。
这里就须要借助babel-polyfill:

npm install --save @babel/polyfill

留神这里不要加-dev,因为这个是在代码运行时也须要的。

咱们只须要在须要polyfill的中央import就能够了,这里咱们把这个import放到业务代码index.js的顶部

// index.jsimport "@babel/polyfill";const arr = [];arr.map(...)

这里其实这种写法不太好,这其实把所有的polyfill函数都1打包进去了,会发现build之后的包特地大,这里咱们不心愿babel把所有兼容语法都写进去,例如只心愿polyfill一下map办法,不然包太大了

这里须要一个配置写进去webpack.config.js,在外面对presets字段进行更改,增加一个配置:

rules:[{    test:/\.js$/,    exclude:/node_modules/,    loader:"babel-loader",    options:{        presets:[["@babel/preset-env",{useBuiltIns:'usage'}]]    }}]

意思是当我做polyfill不是一股弄都加进去,而是看用到什么才加进去,而后再打个包:

留神点:如果你开发的是一个类库或者第三方模块,就不倡议采纳这种形式,因为polyfill是采纳全局替换的办法实现兼容的,类库等应用这种形式会净化到全局环境。

3.4 babel/plugin-transform-runtime

打包类库须要换一种配置形式:

这里咱们须要装置两个模块,依照官网阐明。
接着你须要在webpack配置参数外面加一个plugin:

rules:[{    test:/\.js$/,    exclude:/node_modules/,    loader:"babel-loader",    options:{        "plugins":[["@babel/plugin-transform-runtime",{            "corejs":2,            "helpers":true,            "regenerator":true,            "useEsModules":false        }]]    }}]

而后从新打包,这时候会提醒报错,短少一个corejs2的模块,这是因为咱们的corejs写的是2,这时候参考文档再装置一个包:

npm install --save @babel/runtime-corejs2

而后就能够了。
如果你写的是业务代码,参考3.3应用polyfill就能够了。

如果你写的是类库,参考上述去做,防止净化全局环境。

3.5 解决options配置项过多问题

线上业务很可能options配置会很多,这时候咱们能够根目录下独自写一个.babelrc文件,把配置项复制进去:

// .babelrc{    "plugins":[["@babel/plugin...."]]}

3.6 配置React代码的打包

React自带有JSX的语法,如果不进行非凡解决的话webpack是没方法辨认的,其实Babel外面就有工具能够帮忙咱们解析React种的JSX语法:

npm install --save-dev @babel/preset-react

而后再presets字段外面增加@babel/preset-react就能够了。

{    presets:[        [            "@babel/preset-env",{                targets:{                    chrome:'67',                },                useBuiltIns:'usage'            }        ],        "@babel/preset-react"    ]}

留神:Babel外面语法本义是有执行程序的,从下往上,从右往左,先转换react代码,而后把ES6转换为ES5。重启Devserver即可