Webpack4.0学习笔记

webpack 到底是什么?

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),当 webpack 处理应用程序时,会递归构建一个依赖关系图,其中包含应用程序须要的每一个模块,而后将这些模块打包成一个或多个 bundlecss

传统模式开发

新建 index.html,以下:html

<!DOCTYPE html>
<html lang="en">
<body>
  <p>这是网页内容</p>
  <div id="root"></div>
  <script src="./header.js"></script>
  <script src="./content.js"></script>
  <script src="./footer.js"></script>
  <script src="./index.js"></script>
</body>
</html>
复制代码

再创建index.jsheader.jscontent.jsfooter.jsvue

//index.js
new Header()
new Content()
new Footer()

//header.js
function Header () {
  var dom = document.getElementById('root')
  var header = document.createElement('div')
  header.innerText = 'header'
  dom.append(header)
}

//content.js
function Content () {
  var dom = document.getElementById('root')
  var content = document.createElement('div')
  content.innerText = 'content'
  dom.append(content)
}

//footer.js
function Footer () {
  var dom = document.getElementById('root')
  var footer = document.createElement('div')
  footer.innerText = 'footer'
  do
m.append(footer)
}
复制代码

这样面向对象new出各个模块的构造函数,在必定程度上比面相过程所有写在一个文件上好维护,可是HeaderContet这些构造函数指向问题还要回到index.html文件一一对应查找,就形成了在index.js上指定不明确,若是咱们能像如下代码同样导入就能解决这个问题了node

//index.js
import Header from './header.js'
import Content from './content.js'
import Footer from './footer.js'

new Header()
new Content()
new Footer()
复制代码

遗憾的是,浏览器不会识别这种es6的语法,使用浏览器访问index.html会发现控制台会报错。此时咱们的webpack就能够为了解决这种问题而诞生了webpack

使用webpack模块化开发

首先想使用webpack,咱们得先npm初始化包并安装webpackcss3

npm init
npm install webpack webpack-cli -D
复制代码

想用ESmodule 语法导出,咱们得修增长导出语法es6

//header.js
export default Header

//content.js
export default Content

//footer.js
export default Footer
复制代码

而后咱们就可使用npx 运行 webpack 执行 index.js 文件web

npx webpack index.js
复制代码

执行后,咱们发发现根目录下生成了一个 dist/main.js目录文件,这即是webpack默认出口输出(output),后面会介绍。这个文件即是webpack翻译咱们的index.js生成后的文件,因此有的人会称webpackJS的翻译器,但这说法也并不完成正确。既然咱们翻译了文件,咱们就须要在index.html修改为导入后的文件,以下修改:npm

//index.html
<script src="./dist/main.js"></script>
复制代码

修改完成后打开浏览器从新访问index.html发现能够生成相对于模块而且没有报错了,这就是webpack的模块开发。json

使用webpack的配置文件

webpack 的核心概念

  • entry: 入口

  • output: 输出

  • loader: 模块转换器,用于把模块原内容按照需求转换成新内容

  • 插件(plugins): 扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或作你想要作的事情

使用一个配置文件

webpack v4 中,能够无须任何配置,由于webpack提供了默认配置,然而大多数项目会须要很复杂的设置,这就是为何 webpack 仍然要支持 配置文件。这比在 terminal(终端) 中手动输入大量命令要高效的多,因此让咱们在根目录建立一个配置文件:webpack.config.js(默认配置文件,可经过npx webpack --config webpack.config.js修改)

//webpack.config.js
const path = require('path')
module.exports = {
  entry: './src/index.jss' , //入口文件 默认:src/index.js
  output: { //出口文件 默认: dist/main.js
    filename: 'bundle.js', //输出的文件名 
    path: path.resolve(__dirname,'dist') //输出的路径,只能是绝对路径
  }
}
复制代码

新建src文件夹,移动index.js,header.js等js到src文件夹,执行如下代码会发现dist文件夹生成了新的出口文件bundele.js

npx webpack src/index.js
复制代码

entry

配置须要打包入口文件

//webpack.config.js
<!--单个文件-->
module.exports = {
  entry: './src/index.jss' , //入口文件 默认:src/index.js
}

<!--打包多个入口文件-->
module.exports = {
 entry: {
    main: './src/index.js', //入口文件 默认:src/index.js
    sub: './src/sub.js'
  },
}
复制代码

output

配置打包输出的文件

output: { //出口文件 默认: dist/main.js
    filename: 'bundle.js', //输出的文件名 
    path: path.resolve(__dirname,'dist') //输出的路径,只能是绝对路径
  }
  
  <!--多个入口文件须要不一样名称文件输出配置-->
  output: { //出口文件 默认: dist/main.js
    filename: '[name].js', //输出的文件名 
    path: path.resolve(__dirname, 'dist') //输出的路径,只能是绝对路径
  },
复制代码

npm scripts

考虑到用 CLI 这种方式来运行本地的 webpack 副本并非特别方便,咱们能够设置一个快捷方式。调整 package.json 文件,添加在 npm scripts 中添加一个 npm 命令:

//package.json
"scripts": {
    "bundle" : "webpack"
},
复制代码

如今,可使用 npm run build 命令,来替代咱们以前使用的 npx 命令。注意,使用 npm scripts,咱们能够像使用 npx 那样经过模块名引用本地安装的 npm packages。这是大多数基于 npm 的项目遵循的标准,由于它容许全部贡献者使用同一组通用脚本(若是必要,每一个 flag 都带有 --config 标志)。

如今运行如下命令,而后看看你的脚本别名是否正常运行:

npm run bundle
复制代码

浅析webpack打包输出内容

npm run bundle

> webpack4@1.0.0 bundle E:\project\webpack4
> webpack

Hash: 768c04b37ed214487576
Version: webpack 4.42.0
Time: 98ms
Built at: 2020-03-24 4:57:54 PM
    Asset      Size  Chunks             Chunk Names
bundle.js  1.29 KiB       0  [emitted]  main
Entrypoint main = bundle.js
[0] ./src/index.js + 3 modules 741 bytes {0} [built]
    | ./src/index.js 147 bytes [built]
    | ./src/header.js 202 bytes [built]
    | ./src/content.js 198 bytes [built]
    | ./src/footer.js 194 bytes [built]


WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
复制代码

webpack打包输出的结果咱们能够看出有个警告,那是由于咱们没有指定打包输出的环境('development' or'production'),咱们能够在webpack.config.js新增如下代码:

module.exports = {
  mode: 'development', //默认为production
  ...
}
复制代码

从新执行npm run bundle 发现不会提出警告了,而且生成的bundle.js的代码没有被压缩

什么是 loader

webpack 可使用 loader 来预处理文件。这容许你打包除 JavaScript 以外的任何静态资源。若是你在默认配置下打包除js文件外出错,因此咱们要借助loader来打包js外的文件

如今咱们在src文件下存放一张logo.jpg的图片,而后在index.js引入后使用webpack打开

//index.js
const logo = require('./logo.jpg')
复制代码

执行npm run bundle打包后会抛出以下错误:

ERROR in ./src/logo.jpg 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
 @ ./src/index.js 5:13-34
复制代码

这是由于webpack像咱们上面说的同样,只能打包js文件,若是想打包除js之外的静态资源,此时咱们就须要借用loader来帮助咱们打包图片资源。想打包图片咱们首先得先安装相关的loader

file-loader

npm install file-loader -D
复制代码

而后咱们须要在webpack.config.js文件下配置相关的module规则:

//webpack.config.js
...
module: {
    rules: [
      {
        test: /\.jpg$/,
        use: [
          {
            loader: 'file-loader' //使用相对应的loader
          }
        ]
      }
    ]
  }
复制代码

控制台npm run bundle再次运行时,发现打包成功没有报错,而且在dist出口目录生成了相应的图片资源。那咱们导入的图片资源变量会是什么呢,咱们试着打印一下:

const logo = require('./logo.jpg')
console.log('logo',logo)
复制代码

能够在浏览器中看出,咱们获取到的是打包生成的文件相关信息,包括图片名字。通过这个例子,咱们就明白了在 vue的脚手架项目中能够这样引入.vue相关文件的了

import Header from 'Header.vue'
复制代码
//webpack.config.js
rules: [
    ...
      {
        test: /\.vue$/,
        use: { //只使用一个loader能够直接用对象配置
          loader: 'vue-loader'
        }
      }
    ]
复制代码

上面案例中,咱们能够看到options字段,这是咱们能够打包文件资源的时候定义相对应的规则,好比:

rules: [
      {
        test: /\.jpg$/,
        use: [
          {
            loader: 'file-loader', //使用相对应的loader
            options: {
              name:'[name].[ext]', //定义打包生成的文件名字
              outputPath: 'images' //定义输出的文件目录:dist/images 下
            },
          }
        ]
      }
    ]
复制代码

这样咱们就能够自定义打包文件的名字和目录,更多的规则能够查看官方文档配置

url-loader

咱们能够用url-loader取代file-loader来实现静态文件资源打包,那为何咱们有file-loader还要用url-loader,让我看看下面的例子就知道了:

首先咱们先npm install url-loader --save-dev安装file-loader,而后在webpack.config.js进行相对应的配置

rules: [
      {
        test: /\.jpg$/,
        use: [
          {
            loader: 'url-loader', //使用相对应的loader
            options: {
              limit: 10240 //单位:字节 超过10kb 的文件生成图片,不然生成base64编码
            }
          }
        ]
      }
    ]
复制代码

运行打包后咱们能够发现,超过10kb 的文件生成图片,不然生成base64编码。这样作的好处是什么呢,图片生成base64编码能够大大减小咱们https请求,但不是全部的图片都生成base64编码,好比图片几M的生成的话,相对应js文件大小也会增长,网页打空白的时间也相对应增长,至于哪些须要转,limit须要设置多大限制根据本身的项目来,个人项目中通常是icon图标类的会转base64编码,其余大的相对应打包生成文件。

使用loader打包样式静态资源

当咱们尝试打包css文件的时候,若是没有使用相对应的样式loader就会打包失败。咱们在src目录下建立新的文件index.csslogo.css,而且在index.js引入该样式文件:

//logo.css
.logo{
  width: 100px;
  height: 100px;
}
复制代码
//index.css
@import './logo.css'
复制代码
//index.js
import logo from './logo.jpg'
import './index.css'

var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
复制代码

打包以后能够看到控制台抛出了相对应的报错,此时咱们应该接入css-loaderstyle-loader来解决这个问题,首先咱们先得安装两个loader

npm install css-loader style-loader -D
复制代码
rules: [
    ...
      {
        test: /\.css$/,
        //注:打包执行顺利从右到左,从下到上,不能颠倒,先接css-loader转换语法在使用style-loader解析到头部标签
        use: ['style-loader','css-loader'] 
      }
    ]
复制代码

css-loader

主要用于打包css文件中解析@import等语法,将 CSS 转化成 CommonJS 模块。

css-loader还能够配置更多的选项,好比importLoadersmodules等。若是没有配置importLoaders,在一个scss文件中@import其余的scss文件,可能该导入的scss文件不会生效css-loader后面配置的loader(sass-loader,postcss-loader)

use: ['style-loader',{
          loader: 'css-loader',
          options: {
            importLoaders: 2 // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
          }
        },'sass-loader','postcss-loader'
    ]
复制代码

配置modules参数为true能够模块化导入相关的样式文件,不然会全局样式污染。以下列案例:

//index.js
import logo from './logo.jpg'
import './index.scss'
import createLogo from './logo.js'

createLogo()
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
复制代码
//logo.js
import logo from './logo.jpg'
function createLogo () {
  var img = new Image()
  img.src = logo
  img.classList.add('logo')
  var root = document.getElementById('root')
  root.append(img)
}
export default createLogo
复制代码

上面由于没有配置相关的样式模块导入,因此导入index.scss文件的样式都在两张图片成功生效,下面咱们增长下模块配置引入:

//webpack.congig.js
      ...
        test: /\.scss$/,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            importLoaders: 2, // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
            modules: true //按模块化引入
          }
        }, 'sass-loader', 'postcss-loader'
        ]
      }
复制代码
//index.js
import logo from './logo.jpg'
import style from './index.scss'
import createLogo from './logo.js'

createLogo()
var img = new Image()
img.src = logo
img.classList.add(style.logo)
var root = document.getElementById('root')
root.append(img)
复制代码

从新打包后,咱们发现只有index.js文件的图片生效了样式,咱们模块化导入样式成功,更多的options配置样式能够查看官方文档

style-loader

配合css-loader使用,以形式在html页面中头部标签插入css代码。

sass-loader

npm install sass-loader node-sass webpack --save-dev
复制代码

咱们除了安装sass-lader外,而且还须要你预先安装 Node Sass。 这能够控制全部依赖的版本, 并选择要使用的 Sass 实现。新建src/index.sass

//index.sass
body {
  .logo{
    width: 100px;
    height: 100px;
  }
}
复制代码
//index.js
import logo from './logo.jpg'
import './index.sass'

var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
复制代码

咱们须要在webpack.config.js新增相对应的规则配置:

//webpack.config.js
 rules: [
    ...
      {
        test: /\.sass$/,
        use: ['style-loader','css-loader','sass-loader'] //先把sass转成css ,再进行起来的loader操做(右到左)
      }
    ]
复制代码

配置后从新执行npm run bundle打包,在浏览器中能够正常访问,把sass-loader去掉再打包后,能够查看控制台头部样式中sass的语法没有转成css,这就是sass-loader的做用

postcss-loader

npm install postcss-loader -D
复制代码

postcss-loader能够对css3样式前缀自动补全,兼容各个浏览器,使用postcss-loader前咱们得配置相关的插件等,根目录下新建postcss.config.js,安装相对应的插件:autoprefixer(补全css3语法插件)

npm install autoprefixer -D
复制代码
//postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}
复制代码
//webpack.config.js
rules: [
  ...
  {
    test: /\.scss$/,
    use: ['style-loader','css-loader','sass-loader','postcss-loader']
  }
]
复制代码

autoprefixer补全得结合browserslist一块儿使用

//package.json
"browserslist": [
    "defaults",
    "ie >= 10",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
 ],
复制代码

使用plugins让打包更快捷

插件(plugins): 扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或作你想要作的事情,就相似vue生命周期钩子同样,在某种场景,帮你作某些事情。官方已收录的插件

HtmlWebpackPlugin

一种用于打包生成html的插件:HtmlWebpackPlugin会在打包结束后,自动生成html文件,并把打包生成的js自动引入到这个html文件中。具体配置可查看HtmlWebpackPlugin文档

//安装HtmlWebpackPlugin文档
npm install --save-dev html-webpack-plugin
复制代码
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    ...
    plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html' //生成的模板文件
    }),
    
  ]
}
复制代码
//public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>html 模板</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
复制代码

CleanWebpackPlugin

有时候咱们打包老是要手动删除掉上一次打包的文件,咱们就想有没有什么工具能帮助咱们在打包前自动删除掉dist目录,CleanWebpackPlugin就能够帮咱们解决这个问题,详细配置

//webpack.config.js
  ...
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './public/index.html'
    }),
  ]
复制代码

source-map

source-map 能够解决打包后代码报错的地方是打包后的代码而不是源业务代码的问题

//index.js
console.log('devtool',test)
复制代码

这是未设置source-map报错的打包代码

咱们在webpack.config.js 配置下

devtool: 'source-map'
复制代码

配置后从新打包久能够看到报错的是原业务代码,可是咱们不建议直接用source-map,我建议开发环境使用eval-cheap-module-source-map,生成环境用cheap-module-source-map,不一样的配置打包的速度不同,能够简单总结,source-map 会生成.map文件来映射,打包速度会很慢,由于还要映射打包文件,inline能够不生产.map文件,直接打包在出门文件里面转成和base64,cheap能够只报行除出错而不加列出错,module 可让第三方loader 插件也生效报错,eval能够直接执行eval函数因此速度最快,具体能够参考官方文档配置

webpackDevServer

有时候咱们修改了打包入口的文件,老是要从新打包编译打开浏览器访问,有没有一种配置能让咱们监听到入口文件修改,就能自动打包编译在浏览器刷新呢,webpackDevServer就能够帮你作到,webpackDevServer会在本地帮你的项目搭建一个服务器来跑,只要你更新它就能够帮你从新打包编译~

首先咱们得安装webpackDevServer

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

而后配置相关的参数

//webpack.config.js
module.exports = {
 ...
  devServer: {
    contentBase: './dist',
    port: 9000, //服务端口号
    open: true, //首次打包编译自动打开浏览器
    proxy: {//反向代理,通常用于解决跨域问题
      '/api': 'http://localhost:3000'
    }
  },
复制代码

执行npm run start 就能够打包编译帮你打开相关的服务了~

//package.json
"scripts": {
    "start": "webpack-dev-server"
 },
复制代码

Hot Module Replacement

有时候咱们须要作的是,改了该模块的代码,浏览器不刷新,只更新该的模块代码上去,Hot Module Replacement就能帮咱们实现这个效果。

//webpack.config.js
const webpack = require('webpack')
 devServer: {
    ...
    hot: true,//使用 Hot Module Replacement
    hotOnly: true, //Hot Module Replacement 出错的时候,浏览器照样不刷新
  },
  plugins: [
    ...
    new webpack.HotModuleReplacementPlugin() 
  ]
复制代码

js模块代码更新的话还须要增长,css模块的话css-loader已经帮咱们处理了,那像vue的文件修改vue-loader也已经作了相关的处理

if (module.hot) {
  module.hot.accept('./number.js', function() {
    // Do something with the updated library module...
  });
}
复制代码

持续学习更新中...

相关文章
相关标签/搜索