大声对webpack4.0说声你好之参见plugin与经常使用配置详解(三)

在学会使用loader以后,咱们再来看看webpack的plugin的使用。css

导读

本章知识点:plugin/babel/settinghtml

直通车-> webpack插件介绍vue

若是你还不会使用webpack的基本使用和loader的使用,建议您先观看个人大声对webpack4.0说声你好前两个章节。由于咱们例子就是结合上部分的继续讲,只要你跟着个人文章一直 敲代码,你就能很轻松的学会webpack。node

使用插件生成html

在以前的例子中,咱们都是经过npx webpack xxx打包以后,在dist目录下手动的新建一个xx.html,而后预览咱们的效果,这样的操做就比较麻烦,这个时候咱们就须要经过插件的方式来打包,让咱们的效率增倍。webpack

删除个人dist目录。es6

而后安装插件web

npm install --save-dev html-webpack-plugin
复制代码

修改一下webpack.config.js的配置chrome

const htmlPlugin = require('html-webpack-plugin'); // 引入html打包插件

plugins: [
    new htmlPlugin()
  ],
复制代码

引入了个人实例化对象。而后我再来进行一次打包。这个时候就直接给咱们生成了dist目录,而且在dist目录下主动为咱们生成了index.html,而后打开就能够预览到咱们的效果。express

做用:htmlWebpackPlugin会在打包结束后,自动生成一个html文件,并把打包后生成的js自动引入到这个html中。npm

回想以前的例子,咱们若是将一个元素挂载到id=root的div下面的话,其实咱们这个index.html中是并无生成root这样的div的,那这个时候咱们又应该怎么作呢?

修改插件的配置以下,将咱们的本身的index.html做为模板文件,这时就会使用咱们本身的模板文件的内容进行打包。

plugins: [
    new htmlPlugin({
      template: './index.html'
    })
  ],
复制代码

这个时候是否是插件就跟vue的声明周期同样,在你打包的时候,去完成一些事情。

打包前自动删除dist目录

咱们先作一个小测试,就是咱们改变一下咱们输入的js名称。

output: {
    filename: 'dist.js',  // 以前是main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夹
  }
复制代码

dist目录打包以后

而后进行打包操做。而后发现咱们在dist目录下生成了一个dist.js这个的确是我想要的,可是其余的生成内容好像并非我但愿的,由于我若是之后东西多了,我根本就不知道哪些是我想要的内容,因此咱们在打包以前,我但愿删除dist目录,这样就能保证里面的内容都是我所须要的。

clean-webpack-plugin
// 安装插件
npm install clean-webpack-plugin -D

// 引入插件
const { CleanWebpackPlugin } = require('html-webpack-plugin'); 

plugins: [
    new htmlPlugin({
      template: './index.html'
    }),
    new CleanWebpackPlugin()
  ],
复制代码

ok,继续打包。这样就只会有本次打包的内容。

插件小总结

插件的内容比较简单,可是咱们每次打包的时候应该如何去找到咱们的插件呢?webpack的官网是有不少插件的,可是咱们详细的去读确定是有些难度,因此每次使用以前能够百度、谷歌一些打包内容,而后搜索相应的插件进行下载。

Entry与Output的基础配置

Entry入口配置

先回顾一下第一节的知识,知道这两个名词究竟是什么?

大声对webpack4.0说声你好之webpack的基本使用(一)

entry: 打包的入口文件(npx wbpack 就能够直接打包, 不声明每次打包以前须要告诉webpack你想打包哪一个文件)

output:输出文件名称 (打包js以后,默认生成的文件名称,不说明默认是main.js)

so easy.

那我若是要变动一下需求,我将index.js打包两次,分别叫main.js/sub.js,那咱们应该怎么作的?

entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
复制代码

entry配置项详解:官网entry详解直通车

output

打包不出意外的失败了,由于咱们定义了两个文件,可是打包的输入内容只有一个dist.js,因此咱们能够修改一下输入的配置。还记得第二节中讲到的占位符吗?

大声对webpack4.0说声你好之loader基础篇资源打包讲解(二)

output: {
    filename: '[name].js',  //  name 就是至关于entry文件对应的key,既main sub
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夹
  }
复制代码

而后神奇的发现,咱们的index中生成了sub与main两个js文件,这个是由于咱们以前使用了htmlwebpackplugin插件,他帮咱们自动引入了。

<!doctype html><html><head><meta charset="utf-8"><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><script src="main.js"></script><script src="sub.js"></script></body></html>
复制代码

咱们继续升华一下咱们的此次打包操做,好比我打包以后,咱们文件以后是要放在第三方的cdn上面的,这样的话就会在打包文件的前面自动加上一个cdn的域名,这个时候又该怎么办呢?固然手动改是不可能的~!

output: {
    publicPath: 'http://cdn.com/',
    filename: '[name].js',  // 打包后生成的main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夹
  }
复制代码

再次打包(npm rub build -- 我本身修改的scripts,详见系列一)

<script src="http://cdn.com/main.js"></script>
  <script src="http://cdn.com/sub.js"></script>
复制代码

It's perfect.

output配置项详解:官网output详解直通车

SourceMap

在介绍这个配置以前,咱们仍是先进入一个例子,他会让你很清楚的知道SourceMap能帮你干什么。

新建一个source.js,而后修改一下咱们以前修改的配置

// source.js
consele.log('hello, axin !') // console故意写错

// webpack.config.js

// 咱们如今的模式是开发者模式,默认是配置了SourceMap的,因此咱们须要本身关闭
mode: 'development',
devtool: 'none',
  
  
entry: {
    main: './src/source.js'
  },
  
output: {
    filename: 'dist.js',  // 打包后生成的main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夹
  }
复制代码

即便咱们写错了,咱们的打包依然能够继续完成,可是在页面中说咱们的consele未定义,而后点击进去发现是打包以后的文件报错,可是我并不知道我原文件中是哪一行报错,否则我都不知道去哪里修改。

SourceMap:映射关系,它知道dist目录下打包文件的报错信息具体是你src目录下的哪一行

devtool: 'source-map'
复制代码

这个时候打包,他就会告诉咱们src下面的source文件的第一行报错了。并且咱们会在dist目录下生成一个dist.js.map的文件,他就是咱们文件的对应关系,若是使用inline-source-map,就不会出现map文件,可是会有一个base64的字符串做为映射联系。自觉上车->官方直通车

推荐形式: 开发环境中,cheap-module-eval-source-map,打包速度快,提示也比较全。

那若是开发的代码放到线上,出现的问题,咱们应该怎么配置呢?cheap-module-source-map

webpackDevServer

目前的操做中,咱们是修改了咱们的代码,而后使用npm run build,最后在dist文件下,找到咱们的index.html,在浏览器中打开。那么如此麻烦的操做咱们应该如何优化从而提升开发效率呢?

"scripts": {
    "build": "webpack",
    "watch": "webpack --watch"
  },
复制代码

build是我本身新增的打包代码命令,详解在系列一。

watch能够监听咱们的源代码发生变化,他就会自动帮咱们从新打包。

因此咱们直接使用命令,就能够实现咱们的该操做。

npm run watch
复制代码

监听源文件变化从新打包,而后渲染到咱们页面,而后刷新就能够用了,可是这样操做还不够好,由于我但愿我在第一次使用这个命令的时候,自动帮我打包,而后打开浏览器,还能模拟一些服务器上的特性。

这个时候咱们就能够借助webpackdevserver来达到更加酷炫的效果。

先安装webpack-dev-server

npm install webpack-dev-server- d
复制代码

修改配置项

// webpack.config.js
devServer: {
    contentBase: './dist', // 借助webpack启动服务器,根目录就是打包以后的dist文件夹
    open: true // 启动npm run start的时候自动打开浏览器
  },
  
// package.json
"scripts": {
    "build": "webpack",
    "watch": "webpack --watch",
    "start": "webpack-dev-server"
  },
复制代码

而后咱们在执行npm run watch,修改源代码,就不须要从新打开浏览器进行手动刷新,webpack-dev-server已经帮咱们作了这些操做,因此咱们只关注咱们的业务代码就ok。

继续扩展,gogogo!

在vue中,咱们常用proxy来代理咱们的跨域服务,由于在webpack中就是支持这种操做的。

// 跨域转发代码
proxy: {
  '/api': 'http://localhost:3000'  
}

// 配置端口号
port: 8080
复制代码

官网直通车:www.webpackjs.com/configurati…

接下来咱们看一个比较高逼格的操做。

手写本身的server

新建一个scripts命令

"scripts": {
    ...
    "server": "node server.js"
  },
复制代码

由于用到了node,因此我使用expres和一个webpack-dev-middleware

npm install express webpack-dev-middleware -D
复制代码

修改一个webpack配置确保个人文件打包到根目录下

output: {
    publicPath: '/',
    filename: 'dist.js',  // 打包后生成的main.js
    path: path.resolve(__dirname, 'dist'), // 打包到dist文件夹
  }
复制代码

咱们就会在根目录下新建一个server.js,运行npm run server的时候就会进入咱们本身的脚本

// 引入express webapck 中间件
var express = require('express')
var webpack = require('webpack')
var webpackDevMiddleware = require('webpack-dev-middleware')
// 引入webpack配置文件
var config = require('./webpack.config.js')
var complier = webpack(config) // config配置传入 complier主要作编译

const app = express()
app.use(webpackDevMiddleware(complier, {
  publicPath: config.output.publicPath // 声明本身的publicPath
}))

app.listen('3000', () => { // 监听3000端口启动成功 
  console.log('server is running')
})
复制代码

在使用 npm run server 就能够启动本身的服务命令了。

代码比较多,我打了一些注释,可能不少人仍是看不懂,我再来梳理一下这个流程。

server使咱们手写的一个相似于wbpack-dev-server的功能,因此咱们在运行npm run server的时候,其实是运行了一个js文件,利用express咱们建立了一个server应用,端口是3000,webpackDevMiddleware是webpack的中间件,complier返回的是一个编译器,只要文件改变了就会从新编译。

因此webpack不止在命令行中运行,还能够在nodejs中运行。

一行代码声明入口文件与出口文件

webpack index.js -o dist.js
复制代码

这句话就是打包index.js后打包文件是dist.js

Hot Module Replace

devserver的好处

热模块替换,简写HMR

咱们先在配置中修改一下配置

// 删除打包的dist目录

// 以前的package.json 删除build,server,
"build": "webpack",
"watch": "webpack --watch",
"start": "webpack-dev-server",
"server": "node server.js"

// 只保留一个start
"scripts": { 
    "start": "webpack-dev-server"
  },
  
// 启动服务
npm run start

复制代码

这个时候咱们会发现,webpack-dev-server并无在咱们的根目录下生成一个dist目录,可是咱们的代码还能够正常运行,这个过程当中webpack其实也是对咱们的代码进行的代码,不过文件在咱们的内存中,因此打包的效率更高。

css热更新

继续,咱们在src新建btn.js,statics/style/btn.css

// btn.js
var btn = document.createElement('button')
btn.innerHTML = '新增'
document.body.appendChild(btn)
btn.onclick = function(){
  var div = document.createElement('div')
  div.innerHTML = 'item'
  document.body.appendChild(div)
}

// btn.css
div:nth-child(2n){
  background-color: yellow;
}
复制代码

而后修改入口文件问btn.js,这段代码就是在body里面新增一个btn按钮,点击一次新增一个item的div。偶数的div背景会是一个黄色。

可是咱们若是去修改css中背景色,这个时候浏览器就会自动刷新,以前点击新增的div已经彻底没有了,这样咱们就会从新点击新增来添加节点,这样就会很是麻烦,那么咱们在修改css的时候,能保证只是从新加载样式的话,就好多了。

在devServer中添加以下配置。

const webpack = require('webpack') // 引入webpack插件

devServer: {
    contentBase: './dist', // 借助webpack启动服务器,根目录就是打包以后的dist文件夹
    open: true, // 启动npm run start的时候自动打开浏览器
    proxy: { // 配置代理
      '/api': 'http://localhost:3000' 
    },
    port: 8080, // 配置端口号
    hot: true, // 开启热更新
    hotOnly: true // 就算是html文件没生效也不刷新页面
  },
  
plugins: [
    new htmlPlugin({
      template: './index.html'
    }),
    new cleanPlugin(['dist']),
    new webpack.HotModuleReplacementPlugin() // 引入插件 html才能生效
  ],
复制代码

由于改了webpack配置文件内容,因此咱们重启一下服务,这样的话,咱们修改css文件就不会从新刷新页面,只会重载样式。

ok,再来升华一个例子。在src下新建count.js与number.js

html热更新

咱们仍是先注释添加的hot,hotonly以及插件

// webpack.config.js


// count.js
function count(){
  var div = document.createElement('div')
  div.setAttribute('id', 'count')
  div.innerHTML = 1
  div.onclick = function () {
    div.innerHTML = parseInt(div.innerHTML, 10) + 1 // 按照十进制解析+1
  }
  document.body.appendChild(div)
}

export default count

// number.js
function number() {
  var div = document.createElement('div')
  div.setAttribute('id', 'number')
  div.innerHTML = 1000
  document.body.appendChild(div)
}

export default number

复制代码

解释:count方法新增了一个div。id为count,初始文本为1,有一个点击事件,点击一次内容按照十进制解析,而后+1,最后挂载到body上面,导出方法。number方法就本身悟。

而后在btn.js中引入该方法。

import count from './count'
import number from './number'

count()
number()
复制代码

咱们点击count,内容会发生变化,可是咱们去修改number的值,会发现页面刷新了,这个时候就和遇到的css问题同样。

可是咱们开启以后,咱们就会发现,咱们修改了number中的数字以后,页面并无刷新了,这时咱们须要手动在btn.js中加入如下代码。

if(module.hot){
  module.hot.akccept('./number', ()=>{ // 监听文件
    number()
  })
}
复制代码

这样就会监听咱们的number文件中的代码发生了变化,就会从新执行number函数。

新的问题就出现了,由于个人number文件发生了变化,我就从新执行了number函数,而后挂载到了body,可是我并不想这样,我想的是把以前的number修改为个人数便可。

// 在执行方法以前,删除以前的节点
document.body.removeChild(document.getElementById('number'))
复制代码

BABEL

初探

babel在全部的框架中,咱们都能见到他的身影。它能够帮咱们更好的处理es6语法。

为何使用babel

为何要使用babel处理es6语法?由于咱们在大多数浏览器中。例如ie低版本等,代码中写了promise、async await等,他会不识别咱们的语法,这个时候就须要将咱们的es6语法转到es5。方便浏览器阅读执行代码。

babel官方介绍webpack中使用教程:babeljs.io/setup#insta…

1.安装loader

npm install --save-dev babel-loader @babel/core
复制代码

babel-loader相信你们已经很熟悉了,它是帮助咱们使用webpack作打包的一个工具。babel-core是babel的一个核心库,它能让babel去识别js里面的内容,而后把js转换为抽象语法树,在编译转换成新的语法出来。

2.检测文件若是是js文件,那么使用babel-loader来进行语义上的分析

{ 
    test: /\.js$/, 
    exclude: /node_modules/,  // 若是你的js文件在 node_modules里面,就不使用babel-loader
    loader: "babel-loader" 
},
复制代码

3.安装@babel/preset-env文件

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

options: {
  "presets": ["@babel/preset-env"]
}
复制代码

问什么会安装这个文件?当咱们的babel-loader处理js文件的时候,babel-loader只是babel和webpack作通信的,但实际上babel-loader并不会将你把es6语法转成es5语法,你还须要一些其余的模块,才能转换。

这样咱们就配置好了,已经能够将es6转义成es5语法。

打包业务文件

新增p.js并修改配置文件

// 打包入口
entry: {
    main: './src/p.js',
  },
  
// p.js
const arr = [
  new Promise(()=>{}),
  new Promise(()=>{})
]

arr.map(item=>{
  console.log(item)
})
复制代码

编译以后咱们的const 变成了var 可是promise map等仍是有些浏览器是识别不了的。

// 编译后
var arr = [new Promise(function () {}), new Promise(function () {})];
arr.map(function (item) {
  console.log(item);
});
复制代码

这个时候咱们还须要借助polyfill

npm install --save @babel/polyfill
复制代码

安装完成以后咱们只须要在代码以前引入就能够了。把它放到业务代码的最顶部。

import "@babel/polyfill";
复制代码

这个时候在打包,文件就会变得很是大,由于他会自动帮咱们生成promise和map等的实现。

可是在咱们目前的代码中,只须要promse和map的实现,因此咱们还须要增长配置。

options: {
  "presets": [["@babel/preset-env",{
    useBuiltIns: "usage"
  }]]
}
复制代码

相比于以前的430kb,如今还有81kb,代码就精简了不少。

更多配置项:babeljs.io/docs/en/bab…

"targets": {
  "chrome": "67" // 大于67的版本就不注入
}

...
复制代码
打包库文件

若是咱们平时本身作一些东西,彻底可使用polyfill,可是若是作一些比较大的ui库等,这种就不适合了,由于他至关于一个全局使用,会污染到咱们的代码。

npm install --save-dev @babel/plugin-transform-runtime

"plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": false,
        "helpers": true,
        "regenerator": true,
        "useESModules": false,
        "version": "7.0.0-beta.0"
      }
    ]
  ]
复制代码

若是将corejs改为2,须要额外的安装@babel/runtime-corejs2

他会帮你以组件的方式引入,不会污染你的全局变量。

提出babel配置

这个时候咱们的代码就已经很是多了,因此咱们能够将options的代码提出来,新建.babelrc文件放入其中

// .babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "targets": {
          "chrome": "67" // 大于67的版本就不注入
        }
      }
    ]
  ]
}

复制代码

总结

  1. 使用插件,htmlWebpackPlugin/clean-webpack-plugin
  2. webpack.config.js配置讲解
  3. sourcemap文件映射等
  4. webpack-dev-serser热更新等
  5. 强大的babel

写了这么久,收到了很多大神的建议,写文章的水平也比较低,第四节开始慢慢开始改变吧,加油加油奥利给~!

相关文章
相关标签/搜索