目录 §

  • 1. 前置知识 §
  • 2. 用到的工具 §
  • 3. webpack功能 §
  • 4. CRMwebpack §
    • 4.1. 目标一:用webpack转译JS §
    • 4.2 目标二理解文件名中hash的用途 §
    • 4.3 目标三:用webpack生成HTML §
    • 4.4 目标四:用webpack引入CSS §
    • 4.5 目标五:用webpack引入SCSS §
    • 4.6. 目标六:用webpack引入LESSStylusPostcss §
    • 4.7. 目标七:用webpack引入图片 §
    • 4.8. 目标八:用webpackimport() 实现懒加载 §
    • 4.9. 目标九:部署到GitHub Pages §
  • 5. 从webpack看前端工具的历史 §
    • 9. 死掉的前端工具 §
    • 9. 和webpack竞争的工具 §
    • 9. 基于webpack的工具 §
  • 6. 总结 §
  • 4.10. 目标十:自己写一个loader §
  • 4.11. 目标十一:自己写一个plugin §
  • 7. 问答 §
  • 8. 学英语 §

0.安装webpack

  • webpack 的运行,是需要依赖 node.js 的运行环境
  • 官网https://webpack.js.org/
  • Get Started
  • 查看目前有哪几种版本 对应版本号 npm info webpack versions
  • 全局安装npm i -g webpack@4 webpack-cli@3
  • 项目文件安装npm init -y && npm install webpack@4 webpack-cli@3 --save-dev
  • 或者yarn init -y && yarn add webpack@4 webpack-cli@3 webpack-dev-server@3 --dev
  • 或者npm init -y && yarn add webpack@4 webpack-cli@3 webpack-dev-server@3 --dev
  • 不可两者都用

实际的版本号是

  • webpack@4.44.1
  • webpack-cli@3.3.12
  • webpack-dev-server@3.11.0

前端工程化的理解

webpack 模块能以各种方式表达它们的依赖关系

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD definerequire 语句
  • css/sass/less 文件中的 @import 语句。
  • stylesheet url(...) 或者 HTML <img src=...> 文件中的图片链接

1. 前置知识 §

ES6

  • let / const / 箭头函数 / … / class / Promise
  • 继承 / this / 原型

CSS 语和布局

  • flex

MVC 概念

  • Model / View / Controller /EventHub

工具的使用

  • VSCode / WebStorm
  • 终端命令行 npm / yarn / http-server / parcel / git

2. 用到的工具 §

webpack@4安装方法见官网Getting Started

  • webpack后面加个 @4 即可
  • 如果webpack升级到了5, 思路依然有效

webpack-dev-server

  • 用于本地预览
  • parcel 不和 webpack 配套

Yarn

  • 用于build发布

正式进入框架阶段

3. webpack功能与使用 §

一个包含前端的项目,里面可能有多个 .js, 多个 .css , 多个静态图片, 多个其他前端资源。 一些 js 资源彼此之前存在依赖关系,当一个页面需要加载多个 .js 的话,也会拖累整个页面的加载速度

为了解决这个问题,webpack 就把各种各样的静态资源,打包成了一个所谓的 assets. 加快浏览器加载

  • 转译代码(ES6转为ES5,SCSS转为CSS)
  • 构建build
  • 代码打包
  • 代码压缩
  • 代码分析

Vue React Webpack 知识对基础知识的杂糅

使用webpack运行

1
mkdir ./src && touch index.html && cd src && touch index.js

调用本地局部安装的 webpack

1
./node_modules/.bin/webpack --version

自动寻找本地文件夹的 webpack

1
npx webpack
  • npx不能识别路径中的空格

4. CRM学webpack §

  1. 目标一:用webpack转译JS
  2. 目标二:理解文件名中hash的用途
  3. 目标三:用webpack生成HTML
  4. 目标四:用webpack引入CSS
  5. 目标五:用webpack引入SCSS
  6. 目标六:用webpack引入LESSStylus
  7. 目标七:部署到GitHub Pages
  8. 目标八:用webpackfile-loader 引入图片引入图片
  9. 目标九:用webpackimport() 实现懒加载
  10. 目标十:自己写一个loader
  11. 目标十一:自己写一个plugin
  12. 目标十二:用webpack实现模块热替换HMR
  13. 目标十三:用webpack引入TypeScript
  14. 目标十四:用Flow检查器
  15. 目标十五:用webpack引入各大框架Vuw React Electron

4.1. 目标一:用webpack转译JS §

途径

什么是转译JS

  • 分析JS代码,并转译成低版本浏览器可以用的JS
  • ./src/x.js export default 'xxx'
  • ./src/index.js import x from './x.js'; console.log(x)
  • 项目路径中npx webpack,查看dist目录中main.js

初始化 webpack.config.js

去除npx webpack的警告:WARNING in configuration The 'mode' option has not been set...

  • 官网webpack->Configuration
  • 在 Configuration 去除WARNING 设置模式(mode) 生产模式(production) 开发模式(development):

webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './foo.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'foo.bundle.js'
  }
};
  • 再运行npx webpack

生产模式(production) 开发模式(development)区别

  • dist/main.js中,生产模式压缩的代码体积小;开发模式,便于Debug
  • 如果暂时没有影响就不用管
  • 之后详细说明如何切换两者

webpack 配置 entryoutput

修改webpack.config.js> module.exports> entry默认入口文件entry: './src/index.js'

1
2
3
4
5
6
7
8
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js'
  }
};
  • 再运行npx webpack
  • 查看dist路径后多了foo.bundle.js
  • 修改output> filenamemain.js
  • 手动删除原来的dist路径
  • 再运行npx webpack
  • 配置entryoutput,会把./src/路径下的JS文件转译后放到dist路径下
  • 默认目标路径可以不写path: path.resolve(__dirname, 'dist')
  • 入口和出口的配置

4.2 目标二:理解文件名中hash的用途 §

  • 官网webpack->Caching
  • 出口的文件名改为'[name].[contenthash].js'
  • 再运行npx webpack得到main.xxx.js文件名后多了hash值用来HTTP缓存

HTTP响应头中的 Cache-Control

  • 设置缓存头
  • 点开响应头Response Headers中的Cache-Control
  • public, max-age=31536000设置最大缓存一年
  • 缓存过期后,再重新请求

HTTP 缓存的意义

  • 缓存一年
  • 除了index.html外的资源存储在用户浏览器的缓存中
  • 减少连接数,访问时间不用重复下载资源
  • 资源的下载速度之间可差百倍

怎么更新JS CSS之类的资源

  • 对资源文件内容做一个MD5 文件名后加hash
  • '[name].[contenthash].js'就是做这个的
  • 每次修改文件内容,再次打包,得新到的main.xxx.js
  • 文件内容和文件名有一一对应的关系
  • 之前的缓存就没用了
  • 浏览器就会更新资源 ,删除原来的缓存

为什么不缓存index.html

  • 需要在首页中存储更改hash的文件名
  • 通知浏览器其他资源的入口
  • 首页一定不能做缓存,或者设置的最大缓存时间不能太长
  • 链接到 【HTTP非全解】

dist文件越来越多,在 yarn build时设置自动删除并构建

  • package.json> script中设置rm -rf dist && npx webpack
  • 可省略npx,自动寻找webpack,rm -rf dist && webpack

package.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "name": "webpack-demo-1",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "dependencies": {
    "webpack": "4",
    "webpack-cli": "3",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "3",
    "webpack-log": "^2.0.0",
    "webpack-sources": "^1.4.3"
  },
  "scripts": {
    "build": "rm -rf dist && webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
  • yarn build或者npm run build

webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js'
  }
};

4.3 目标三:用webpack生成HTML §

之前先用git提交下代码备份

安装HtmlWebpackPlugin

1
2
# npm install --save-dev html-webpack-plugin
yarn add html-webpack-plugin --dev

配置webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [new HtmlWebpackPlugin()]
};
  • 运行yarn build
  • 自动生成一个空的html,自动引入了index.xxx.js
  • dist目录下的文件都是自动生成的,不可手动修改

生成一个html模板

  • 新建./src/assets目录 index.html
  • 配置webpack.config.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [new HtmlWebpackPlugin({
    title: 'webpack',
    // filename: 'test.html',
    template: 'src/assets/index.html'
  })]
};
  • 修改 index.html 为常用初始模板,之后配置
  • 对应的标签模板<title><%= htmlWebpackPlugin.options.title %></title>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="zh-Hans">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no,viewport-fit=cover">
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>

<body>
  <div></div>
</body>

</html>

x.js

1
export default 'xxx'

index.js

1
2
3
import x from './x.js'
console.log('x')
console.log(x)

可以实现

  • 自动更新html文件名
  • 自动使用配置里的title标签
  • 自动把引入的js标签放到body底部

要点

  • 自动更改文件名
  • 插件HtmlWebpackPlugin 生成 HTML

4.4 目标四:用webpack引入CSS §

webpack 引入模块化 CSS

x.css

1
2
3
body {
  background: orange;
}

x.js

1
2
import './x.css'
export default 'xxx'

index.js

1
import x from './x.js'
  • 编译css时报错
  • ERROR in ./src/x.css 1:5.
  • Module parse failed: Unexpected token (1:5)
  • You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file...

需要css-loader

css-loader

配置webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [new HtmlWebpackPlugin({
    title: 'webpack',
    // filename: 'test.html',
    template: 'src/assets/index.html'
  })],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
};

按报错安装所需的工具模块Module not found: Error: Can't resolve 'style-loader'

1
yarn add style-loader --dev
  • 再运行yarn build
  • style-loadercss-loader解析的CSS代码放到页面的head > style标签中
  • 开发模式下使用,无需用link标签引入CSS文件
  • 没有Cache-Control做缓存,未部署

x.js观察引入的x.css,之前暂时删掉webpack.config.js'style-loader',

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import css from './x.css'
console.dir(css)
console.log(css.toString())

/*
export default function printMe() {
  console.log('I get called from print.js!');
    }
*/
export default 'xxx'
  • cd ..回到上级目录,再运行yarn build
  • cd dist后,使用hs -c-1查看控制台

使用更方便的webpack-dev-server自动刷新部署代替hs -c-1

1
yarn add webpack-dev-server --dev

配置webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [new HtmlWebpackPlugin({
    title: 'webpack',
    // filename: 'test.html',
    template: 'src/assets/index.html'
  })],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
};

package.json

  • --open表示是否打开浏览器,可删除
1
2
3
4
5
6
7
8
...
  "scripts": {
    "build": "rm -rf dist && webpack",
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open",
    "watch": "webpack --watch"
  },
...
  • 运行yarn start
  • 或者运行npm run start
  • 访问端口http://localhost:8080/
  • 测试实时更新页面background: gray;
  • 不需要在开发时频繁yarn build或是手动删除dist
  • 不会创建或依赖dist目录,在内存中运行,自动更新

singletonStyleTag

使用单个的 <style> 标签加载所有css属性,或者是每个css模块一个 <style> 标签

  • webpack style-loader singletonStyleTag
  • 默认值是false(每个css模块一个 <style> 标签)
  • 默认情况下,每一个css文件会变为一个<style>标签
  • 实际加载中,可能有多个<style>标签;
  • 默认值为 false (而不是原文中说的默认情况下启用此选项)
  • 当该值设置为true 时,那么,原本多个<style>标签会被合并成一个<style>标签
  • 官方文档上写的是默认开启,实际上是不开启的

除了用style-loader生成style,也可以吧CSS抽成文件

使用MiniCssExtractPlugin(生产环境下)

1
yarn add mini-css-extract-plugin --dev

配置webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
    title: 'webpack',
    // filename: 'test.html',
    template: 'src/assets/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
      ignoreOrder: false,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        // css-loader
        // use: [ 'style-loader', 'css-loader' ]
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            },
          },
          'css-loader'
        ]
      }
    ]
  }
};

目前实现了

  • MiniCssExtractPlugincss-loader解析的CSS代码放到页面的head > link标签中
  • 实时生成带hashjs文件到script标签中,并放在body标签内的最后
  • Cache-Control适用于生产环境

要点:引入 css的两种方式

  • 开发时可以使用JS生成style,快速,不需要在dist中生成css文件
  • 发布时可以把CSS抽成带hash文件,可以做缓存

如何做到在开发时用第一种方式

发布时,即在生产环境中,用第二种方式

  • 根据模式选择不同的途径
  • 生产环境:yarn build
    • 对应的package.json中:"build": "rm -rf dist && webpack"
    • 做缓存,用MiniCssExtractPlugin生成link标签
    • 引入带hashCSS文件
    • 插入head标签
  • 开发环境:yarn start
    • 对应的package.json中:"start": "webpack-dev-server"
    • 不做缓存,用css-loader生成style标签
    • 插入head标签

终端命令行使用帮助文档webpack --help

  • 未全局安装的话用npx webpack --help
  • 设置配置文件package.json的路径
  • "build": "rm -rf dist && webpack --config webpack.config.prod.js"
  • 意思是,在build时,不适用默认的配置,而是使用webpack.config.prod.js的配置

package.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
  "name": "webpack-demo-1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "webpack": "4",
    "webpack-cli": "3",
    "webpack-dev-middleware": "^3.7.2",
    "webpack-dev-server": "3",
    "webpack-log": "^2.0.0",
    "webpack-sources": "^1.4.3"
  },
  "devDependencies": {
    "css-loader": "^3.6.0",
    "html-webpack-plugin": "^4.3.0",
    "mini-css-extract-plugin": "^0.9.0",
    "style-loader": "^1.2.1"
  },
  "scripts": {
    "build": "rm -rf dist && webpack --config webpack.config.prod.js",
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server",
    "watch": "webpack --watch"
  },
}
  • yarn start
  • 查看dist,目录中index.html是否引入了style标签

使用两个 webpack config 文件,切换开发模式

  • 默认webpack.config.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  output: {...},
  plugins: [
    new HtmlWebpackPlugin({...}),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        // mode: 'development'
        // `yarn start`
        // css-loader
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
};
  • 创建生产环境的webpack.config.prod.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
let HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  output: {...},
  plugins: [
    new HtmlWebpackPlugin({...}),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
      ignoreOrder: false,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        // mode: 'production'
        // `yarn build`
        // MiniCssExtractPlugin
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            },
          },
          'css-loader'
        ]
      }
    ]
  }
};

继承共有属性 webpack.config.base.js

  • 配置继承webpack.config.base.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
    title: 'webpack',
    template: 'src/assets/index.html'
    })
  ]
};

webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const path = require('path');
const base = require('./webpack.config.base.js')

module.exports = {
  ...base,
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.css$/i,
        // mode: 'development'
        // `yarn start`
        // css-loader
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  }
};

webpack.config.prod.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const base = require('./webpack.config.base.js')
module.exports = {
  ...base,
  mode: 'production',
  plugins: [
    ...base.plugins,
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
      ignoreOrder: false,
    }),
  ],
  module: {
    // ...base.module.rules,
    rules: [
      {
        test: /\.css$/,
        // mode: 'production'
        // `yarn build`
        // MiniCssExtractPlugin
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            },
          },
          'css-loader'
        ]
      }
    ]
  }
};
  • yarn build production mode
  • yarn start development mode

用工具插件webpack-merge实现自动切换

配置分离 生产环境构建

环境变量


webpack 实现了

  • 内置babel-loader转译n个JS文件为1个JS文件
  • JS``import CSSstyle/css-loader就转译为style标签
  • MiniCssExtractPlugin转译n个CSS文件为1个CSS文件

loader vs plugin

loaderplugin的区别是

  • 翻译一下,loader是加载器,plugin是插件
  • loader作用加载文件
  • plugin作用扩展/加强功能

举例

  • babel-loader用来加载JS,转移为低版本的JS
  • style-loadercss-loader用来加载CSS,变为页面中的标签
  • new 一个 HtmlWebpackPlugin MiniCssExtractPlugin
  • plugins 需要安装后require() loader直接安装依赖使用

搜英文关键词,选择性地CRM


4.5 目标五:用webpack引入SCSS §

x.css改名为x.scss,在x.js里改为import css from './x.scss'

注意不要覆盖配置中的module

  • rules: []里加一句...base.module.rules,

webpack.config.base.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
let HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
    title: 'webpack',
    template: 'src/assets/index.html'
    })
  ],
  module: {
    rules: [
      {
        test: /\.scss$/i,
        use: [
          // Creates `style` nodes from JS strings
          {
            loader: 'style-loader',
            options: {
              injectType: 'singletonStyleTag'
            }
          },
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Scss to CSS
          {
            loader: 'sass-loader',
            options: {
              // Prefer `dart-sass`
              implementation: require('dart-sass'),
              sassOptions: {
                fiber: require('fibers')
              }
            }
          }
        ]
      }
    ]
  }
}

webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const path = require('path')
const base = require('./webpack.config.base.js')

module.exports = {
  ...base,
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  mode: 'development',
  module: {
    rules: [
      ...base.module.rules,
      {
        test: /\.css$/i,
        // mode: 'development'
        // `yarn start`
        // css-loader
        use: [
          // Creates `style` nodes from JS strings
          base.module.rules[0].use[0],
          // Translates CSS into CommonJS
          base.module.rules[0].use[1],
        ]
      }
    ]
  }
}

webpack.config.prod.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const base = require('./webpack.config.base.js')
module.exports = {
  ...base,
  mode: 'production',
  plugins: [
    ...base.plugins,
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
      ignoreOrder: false,
    }),
  ],
  module: {
    // ...base.module.rules,
    rules: [
      {
        test: /\.scss$/i,
        use: [
          // Creates `style` nodes from JS strings
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            },
          },
          // Translates CSS into CommonJS
          base.module.rules[0].use[1],
          // Compiles Scss to CSS
          base.module.rules[0].use[2],
        ]
      },
      {
        test: /\.css$/i,
        // mode: 'production'
        // `yarn build`
        // MiniCssExtractPlugin
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            },
          },
          'css-loader'
        ]
      },
    ]
  }
};

x.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import './x.scss'

// import css from './x.scss'
// console.dir(css)
// console.log(css.toString())

/*
export default function printMe() {
  console.log('I get called from print.js!');
    }
*/
export default 'xxx'

要点

1
yarn add sass-loader sass dart-sass --dev

4.6. 目标六:用webpack引入PostCSSLESSStylus §

要点

1
2
3
4
5
yarn add less-loader --dev &&
yarn add stylus-loader stylus --dev &&
yarn add postcss-loader --dev &&
yarn add autoprefixer --dev &&
yarn add postcss-cssnext --dev

x.js

1
2
3
4
import './x.scss'
import './y.less'
import './z.styl'
export default 'xxx'

index.js

1
import x from './x.js'

webpack.config.base.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
let HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const styleLoader = {
  loader: 'style-loader',
  options: {
    injectType: 'singletonStyleTag'
  }
}
const scssLoader = {
  loader: 'sass-loader',
  options: {
    // Prefer `dart-sass`
    implementation: require('dart-sass'),
    sassOptions: {
      fiber: require('fibers')
    }
  }
}

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.[contenthash].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
    title: 'webpack',
    template: 'src/assets/index.html'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        // mode: 'development'
        // `yarn start`
        // css-loader
        use: [
          // Creates `style` nodes from JS strings
          styleLoader,
          // Translates CSS into CommonJS
          'css-loader',
        ]
      },
      {
        test: /\.scss$/i,
        use: [
          // Creates `style` nodes from JS strings
          styleLoader,
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Scss to CSS
          scssLoader
        ]
      },
      // rules[1]
      {
        test: /\.less$/i,
        loader: [styleLoader, 'css-loader', 'less-loader'] // compiles Less to CSS
      }
    ]
  }
};

webpack.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const base = require('./webpack.config.base.js')

module.exports = {
  ...base,
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
  },
  mode: 'development',
  module: {
    rules: [
      ...base.module.rules,
    ]
  }
};

webpack.config.prod.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const base = require('./webpack.config.base.js')
const cssLoaderPlugin = {
  loader: MiniCssExtractPlugin.loader,
  options: {
    publicPath: '../',
  },
}

module.exports = {
  ...base,
  mode: 'production',
  plugins: [
    ...base.plugins,
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
      ignoreOrder: false,
    }),
  ],
  module: {
    rules: [
      // ...base.module.rules,
      {
        test: /\.css$/i,
        // mode: 'production'
        // `yarn build`
        // MiniCssExtractPlugin
        use: [
          cssLoaderPlugin,
          'css-loader'
        ]
      },
      {
        test: /\.scss$/i,
        use: [
          // Creates `style` nodes from JS strings
          cssLoaderPlugin,
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Scss to CSS
          base.module.rules[1].use[2],
        ]
      },
      {
        test: /\.less$/i,
        use: [
          // Creates `style` nodes from JS strings
          cssLoaderPlugin,
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Less to CSS
          'less-loader',
        ]
      }
    ]
  }
};

4.7. 目标七:用webpack引入图片 §

index.js

1
2
3
4
5
6
7
import x from './x.js'
const div = document.getElementById('app')
console.log('div')
console.log(div)
div.innerHTML = `
  <img src="./assets/BlkHo.gif">
`

报错 Cannot set property 'innerHTML of null'

  • .innerHTNL前面的div是个null
  • 未取到divconsole.log(div)
  • 注意<div id="app"></div>中的id没有#

src/assets/index.html

1
2
3
4
5
...
<body>
  <div id="app"></div>
</body>
...

yarn start的页面中为请求到图片

  • 因为webpack-dev-server加载的路径并不是当前目录,而是dist目录

怎样加载图片路径

index.js

1
2
3
4
5
6
import x from './x.js'
import gif from './assets/BlkHo.gif'
const div = document.getElementById('app')
div.innerHTML = `
  <img src="./assets/BlkHo.gif">
`
  • import的时什么将由loader决定
  • webpack image-loader,由许多可供使用的
  • 使用 file-loader 引入本地图片
  • 处理顺序是从数组的走后一项开始的
  • image-webpack-loader 插件放在处理顺序的最后,先用其他(url-loader)引入加载网络图片
  • webpack Asset Management
  • webpack file-loader
  • github file-loader

Error: Can't resolve 'file-loader'

  • 手动安装file-loader
  • yarn add file-loader --dev

webpack.config.base.js

1
2
3
4
5
6
7
...,
{
    test: /\.(png|svg|je?pg|gif)$/i,
    use: [
      'file-loader',
    ],
},

index.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import  from './x.js'
import gif from './assets/BlkHo.gif'
import png from './assets/1.png'
console.log(gif)
console.log(png)
const div = document.getElementById('app')
console.log('div')
console.log(div)
div.innerHTML = `
  <img src="${gif}">
  <img src="${png}">
`
  • 自动添加哈希
  • 图片放到本地的assets
  • 使用import得到路径的引用
  • 将引用放到src
  • file-loader的作用就是把文件变成文件路径

4.8. 目标八:用webpackimport() 实现懒加载 资源异步加载 §

当模块数量过多、资源体积过大时,将暂时用不到的模块延迟加载

使得页面初次渲染时用户下载的资源尽可能小

后续的模块等到恰当的时机再去触发加载

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const button = document.createElement('button')
button.innerText = '懒加载'
button.onclick = () => {
  console.log("点击了 button")
  const lazyPromise = import('./lazy.js')
  // 加载时异步的 拿到的是一个 Promise对象实例
  lazyPromise.then((module) => {
    console.log(module)
    const fn = module.default
    fn()
  }, () => {
      console.log("模块加载错误")
  })
}
div.appendChild(button)

4.9. 目标九:部署到GitHub Pages §

本地打包

1
yarn build

本地预览

1
2
cd dist
hs -c-1

上传代码

webpack 一键部署到 github

原来的部署过程

1.将“站点”部署 上传 dist 目录

  • githubPages

2.使用git分支 上传到 gh-pages 分支

1
2
3
4
5
6
rm -f dist
git add . && git commit -am "remove dist" && git push
yarn build

git branch gh-pages
git checkout gh-pages
  • 手滑把.gitignore删除了,重置,撤回上一步
1
git reset --hard HEAD

只保留 dist node_modules .gitignore

1
git add . && git commit -m "remove source code" && git push

拷出 dist 的所有内容

1
2
mv dist/* ./
rm -rf dist

上传网站根目录

1
git add . && git commit -m "remove create website" && git push --set-upstream origin gh-pages

一个分支用来写代码 一个分支用来预览

  • loading 顺时针转 请求 逆时针转 下载
  • git stash 将修改过的文件暂存
  • git checkout master
  • git stash pop

用脚本一键部署 deploy.sh

  • 回到上一个分支 git checkout -
1
2
3
4
5
6
7
8
9
yarn build &&
git checkout gh-pages &&
rm -rf src *.sh *.js *.json yarn.lock *.css *.scss *.less *.styl *.gif *.png  &&
mv dist/* ./ &&
rm -rf dist;
git add . &&
git commit -m "preview gh-pages" &&
git push
git checkout -

以后如果改了代码 需要更新预览,先提交master 在一键脚本

1
sh ./deploy.sh

webpack 一键部署到 gitee

  • 登录网站 新建仓库
  • 点击SSH
  • git remote add origin git@gitee.com:xmasuhai/webpack-demo-1.git中修改origingitee
  • git remote add gitee git@gitee.com:xmasuhai/webpack-demo-1.git
  • git push -u gitee master
  • git remote -v查看仓库

再次新建一个脚本deploy_cn.sh

1
2
3
4
5
6
7
8
9
yarn build &&
git checkout gh-pages &&
rm -rf src *.sh *.js *.json yarn.lock *.css *.scss *.less *.styl *.gif *.png  &&
mv dist/* ./ &&
rm -rf dist;
git add . &&
git commit -m "preview gh-pages" &&
git push gitee gh-pages:master &&
git checkout -

git github gitee gitlab


4.10. 目标十:自己写一个loader §

4.11. 目标十一:自己写一个plugin §


4.12. 目标十二:用webpack实现模块热替换HMR §

  1. 目标十二:用webpack实现模块热替换HMR
  2. 目标十三:用webpack引入TypeScript
  3. 目标十四:使用Flow检查器
  4. 目标十五:用webpack引入各大框架Vuw React Electron
  5. 目标十六:用webpack实现 i118n

5. 从webpack看前端工具的历史 §

  • 链接到 理性看待node

9. 死掉的前端工具 §

  • Grunt 太慢,几死
  • Gulp 速度ok 但没 webpack 繁荣
  • Require.js 几死
  • Sea.js 已死
  • Browserify 已死

9. 和webpack竞争的工具 §

rollup

  • 比 webpack 的打包体积更小
  • 生态不丰富
  • 适合库的开发

parcel

  • 比 webpack 配置简单
  • 公司要求负载的配置,变态的需求
  • 适合demo学习

snowpack

9. 基于webpack的工具 §

@vue/cli

  • 快速创建vue项目
  • 基本不用配置
  • 特殊配置看文档

create-react-app

  • 快速创建react项目
  • 基本不用配置
  • 特殊配置看文档

@angular/cli

  • 快速创建angular项目
  • 基本不用配置
  • 特殊配置看文档

6. 总结 §

术语总结 自我驱动学习法

  • 定一个小目标
  • 尽一切实现
  • 再定一个

要点

  • 不要定太难完成的目标
  • 不要等别人给你定目标

7. 问答 §

问答1:要将webpack学到什么程度

  • 给一个需求就能搞定的程度
  • CRM现有解决方案

问答2:面试问webpack怎么答

  • 相关博客
  • 几个手写webpack config的项目
  • 使用@vue/clicreate-react-app

8. 学英语 §

  • 调手机操作系统语言为英语
  • 订阅Hacker NewsJS Daily
  • 优先看英文文档

工具

  • 词典
  • 抽离 extract



参考文章

相关文章


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名