wabpack自用学习笔记(一)

218 阅读6分钟

阅读的是阮一峰的《深入浅出webpack》这本书
学习网址webpack.wuhaolin.cn/1%E5%85%A5%…

image.png

入门章

一.下载安装

全局安装webpack: npm install webpack -g
项目独立webpack: npm install --save-dev webpack

二.模块化

  • 把一个复杂的东西分解多个模块,并进行组合在一起
  • 模块的内部数据与实现是私有的, 只向外部暴露接口与其它模块通信

Common JS

CommonJs是一个模块化规范,通过 require 方法来同步地加载依赖

// 导入
const module1 = require('./module1');
console.log(module1.b)    //12
// 导出
module.exports = {b:12}

ES6模块化

// 导入
import { readFile } from 'fs';
import React from 'react';
// 导出
export function hello() {};
export default {
  // ...
};

样式文件模块化

SCSS,LESS 把一些常用的样式片段放进一个通用的文件里

// mixin.scss 文件

// 定义样式片段
@mixin center {
  // 水平竖直居中
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%,-50%);
}

// main.scss 文件

// 导入和使用 util.scss 中定义的样式片段
@import "mixin";
#box{
  @include center;
}

框架

vue
Angular2
react

三.使用webpack打包js

新建普通项目,目录如下(dist为空文件夹)

image.png

// module2.js
const module1 = require('./module1');
console.log(module1.b)    

// module1.js
module.exports = {b:12}

// index.html
引入打包后的js文件
<script src="dist/bundle.js"></script>

//main.js为打包入口文件
//只需导入module2,module2已经引入module1了
const module2 = require('./module2');
module2('Webpack');

//webpack.config.js
const path = require('path');
module.exports = {
    entry: './main.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, './dist'),
    }
};

运行webpack,生成打包后的bundle.js文件

image.png

四.使用Loader打包css文件到bundle.js

下载 npm i -D style-loader css-loader

  • css-loader 加载css
  • style-loader css-loader内部样式注入HTML页面

项目新增main.css 在main.js中引入main.css

//main.css
body {
    text-align: center;
}
  
//main.js
const module2 = require('./js/module2');
const maincss =require('./main.css');
module2('Webpack');
maincss('Webpack')

//webpack.config.js
const path = require('path');
module.exports = {
    entry: './main.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, './dist'),
    },
    module: {
        rules:[
            {
            test:/\.css$/,     //正则匹配css文件
            use:['style-loader','css-loader']    //使用style-loader css-loader
            }
        ]
    }
  };

运行webpack

五.用plugin把bundle.js文件里的 CSS 提取出来

书中extract-text-webpack-plugin方法

1.下载 npm i -D extract-text-webpack-plugin

2.webpack.config.js配置

//导入extract-text-webpack-plugin
const ExtractTextPlugin = require('extract-text-webpack-plugin');

 module: {
    rules: [
      {
        test: /.css$/,
        use: ExtractTextPlugin.extract({
          use: ['css-loader'],
        }),
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      //filename: `[name]_[contenthash:8].css`,
      filename: 'mainbundle.css'   //自定义
    }),
  ],

3.最后在index.html中导入新生成的css

但是这种方法只适用webpack@5.0.0以下, 并且webpack@4.X.X的下载方法应为:npm install extract-text-webpack-plugin@next --save-dev

mini-css-extract-plugin方法

webpack@5.0.0以上就得用这个方法了

1.npm install mini-css-extract-plugin --save-dev
2.webpack.config.js配置

//导入mini-css-extract-plugin
const MiniCssExtractPlugiun = require('mini-css-extract-plugin');

//
 module: {
        rules: [{
            test: /\.css$/,
            // use: ['style-loader', 'css-loader']
            use: [MiniCssExtractPlugiun.loader, 'css-loader']
        }]
},
plugins: [
    new MiniCssExtractPlugiun({
        // 从 .js 文件中提取出来的 .css 文件的名称
        // filename: `[name]_[contenthash:8].css`, //随机
        filename: 'mainbundle.css'   //自定义
    }),
]

3.最后在index.html中导入新生成的css

六.DevServer

DevServer会启动一个HTTP服务器用于服务网页请求,同时会帮助启动Webpack 1.下载

npm i -D webpack-dev-server

2.webpack.config.js配置

devServer: {
    static: {
        directory: path.join(__dirname, '/'),
    },
    compress: true,
    port: 9000,
    historyApiFallback: true
},

3.package.json配置

"scripts": {
    "dev":"webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

4.运行npm run dev

注意:如果运行出现问题,大概率是版本问题,如果页面404,大概率是webpack.config.js配置的directory路径不对

配置章

一.Entry

Entry必填的,配置模块入口文件

module.exports = {
    entry: './main.js',
}

类型

  • String './app/entry'
  • array ['./app/entry1', './app/entry2']
  • object
{ 
  a: './app/entry-a', 
  b: ['./app/entry-b1', './app/entry-b2']
}

多入口:每个入口生成一个 Chunk

image.png image.png

Context

寻找相对路径时,以context配置的为根目录
必须是一个绝对路径的字符串
context默认的值就是项目的根目录

module.exports = {
  context: path.resolve(__dirname, '/')
}

Chunk名称

webpack会为每一个生成的chunk起一个名字,Chunk是Webpack打包过程中,一堆module的集合。

  • 如果类型为String和Array,则只生成一个Chunk名字为main
  • 如果类型为object,会生成多个Chunk,用键名起名字

动态Entry

entry接收一个函数

// 同步函数
entry: () => {
  return {
    a:'./a',
    b:'./b',
  }
};
// 异步函数
entry: () => {
        return new Promise((resolve) => {
            resolve({
                a: './a',
                b: './b'
            })
        })
    },

二.Output

Output:如何输出打包的代码

只有一个输出文件

output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist'),
},

多个输出文件时

output: {
    filename: "[name].entry.js",
    path: path.resolve(__dirname, './dist'),
},

除了name(Chunk 的名称),还可以用id(唯一标识,从0开始)、hash(唯一标识的hash值)、chunkhash(Chunk内容的Hash值)

chunkFilename

配置无入口的Chunk在输出时的文件名称

output: {
    filename: '[name].min.js', 
    chunkFilename: '[name].min.js'
}

path

输出文件存放在本地的目录

path: path.resolve(__dirname, 'dist_[hash]')

publicPath

配置异步加载时对应的url地址

filename:'[name]_[chunkhash:8].js'
publicPath: 'https://xxx/assets/'
<script src='https://xxx/assets/a_12345678.js'></script>

crossOriginLoading

webpack输出可能需要异步,异步通过jsonp实现,

libraryTarget 和 library

  • libraryTarget 何种方式导出库
  • library 导出库名称 配置
  1. var(默认)

指定名称的变量
output.library='LibraryName'

// Webpack 输出的代码
var LibraryName = lib_code;
// 使用库的方法
LibraryName.doSomething();
  1. commonjs1

CommonJS 规范导出
output.library='LibraryName'

// Webpack 输出的代码
exports['LibraryName'] = lib_code;
// 使用库的方法
require('library-name-in-npm')['LibraryName'].doSomething();

3.commonjs2

CommonJS2 规范导出\

// Webpack 输出的代码
module.exports = lib_code;
// 使用库的方法
require('library-name-in-npm').doSomething();

4.this

指定的名称

// Webpack 输出的代码
this['LibraryName'] = lib_code;
// 使用库的方法
this.LibraryName.doSomething();

5.window

指定的名称,把库挂载到 window 上

// Webpack 输出的代码
window['LibraryName'] = lib_code;
// 使用库的方法
window.LibraryName.doSomething();

6.global

指定的名称,即把库挂载到 global 上

// Webpack 输出的代码
global['LibraryName'] = lib_code;
// 使用库的方法
global.LibraryName.doSomething();

libraryExport

配置导出的模块中哪些子模块需要被导出,它只有在 output.libraryTarget 被设置成 commonjs 或者 commonjs2 时使用才有意义

// Webpack 输出的代码
module.exports = lib_code['a'];
// 使用库的方法
require('library-name-in-npm')===1;

三.module

配置如何处理模块

Loader

rules 配置模块的读取和解析规则,通常用来配置 Loader

 module: {
        rules: [{
            test: /\.css$/,
            use: [MiniCssExtractPlugiun.loader, 'css-loader'],
            exclude: path.resolve(__dirname, 'node_modules')
        }]
},

传入很多参数时,可以用Object来描述

use: [
  {
    loader:'babel-loader',
    options:{
      cacheDirectory:true,
    },
    // enforce:'post' 把该 Loader 的执行顺序放到最后
    // enforce:'pre',把该 Loader 的执行顺序放到最前面
    enforce:'post'
  },
  .....
]

text(匹配文件类型)、include(只匹配某目录的文件类型)、exclude(排除某目录下的文件 ),这三个能传数组、正则、字符串

rules: [{
            test: [/\.css$/,/\.scss$/],
            use: [MiniCssExtractPlugiun.loader, 'css-loader'],     
            include:[
               path.resolve(__dirname, 'src'),
               path.resolve(__dirname, 'tests'),
            ],
            exclude: [
                     path.resolve(__dirname, 'node_modules'),
                     path.resolve(__dirname, 'bower_modules'),
                     ]
       }]

noParse

忽略对部分没采用模块化的文件的递归解析和处理,提高性能

// 使用正则表达式
noParse: /jquery|chartjs/
// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content)=> {
  // content 代表一个模块的文件路径
  // 返回 true or false
  return /jquery|chartjs/.test(content);
}

parser

webpack内置了对模块化JavaScript的解析功能

  • parser 可以精确到语法层面
  • noParse 只能控制哪些文件不被解析
module: {
  rules: [
    {
      test: /.js$/,
      use: ['babel-loader'],
      parser: {
      amd: false, // 禁用 AMD
      commonjs: false, // 禁用 CommonJS
      system: false, // 禁用 SystemJS
      harmony: false, // 禁用 ES6 import/export
      requireInclude: false, // 禁用 require.include
      requireEnsure: false, // 禁用 require.ensure
      requireContext: false, // 禁用 require.context
      browserify: false, // 禁用 browserify
      requireJs: false, // 禁用 requirejs
      }
    },
  ]
}

四.resolve

resolve 配置 Webpack 如何寻找模块所对应的文件

alias

配置项通过别名来把原导入路径映射成一个新的导入路径

// Webpack alias 配置
resolve:{
  alias:{
    components: './src/components/'
  }
}

当通过 import Button from 'components/button' 导入时,实际上被 alias 等价替换成了 import Button from './src/components/button'

mainFields

如果第三方模块提供了ES5和ES6两个模块,

{
  "jsnext:main": "es/index.js",// 采用 ES6 语法的代码入口文件
  "main": "lib/index.js" // 采用 ES5 语法的代码入口文件
}

根据 `mainFields` 的配置去决定优先采用那份代码
mainFields: ['browser', 'main']
mainFields: ['jsnext:main', 'browser', 'main']

extensions

导入文件没带后缀时,webpack会自动带上后缀后去尝试访问文件是否存在

extensions: ['.js', '.json']  //优先js,然后json

modules

去哪些目录下寻找第三方模块,默认是只会去 node_modules 目录

modules:['./src/components','node_modules']

descriptionFiles

配置描述第三方模块的文件名称,也就是 package.json 文件

descriptionFiles: ['package.json']

enforceExtension

如果配置为 true 所有导入语句都必须要带文件后缀
import './foo' 能正常工作,开启后就必须写成 import './foo.js'

enforceModuleExtension

enforceModuleExtension 只对 node_modules 下的模块生效
在 enforceExtension:true 时,安装的第三方模块中大多数导入语句没带文件后缀, 所以这时通过配置 enforceModuleExtension:false 来兼容第三方模块。

五.Plugin

Plugin 需要的参数通过构造函数传入

const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');

module.exports = {
  plugins: [
    // 所有页面都会用到的公共代码提取到 common 代码块中
    new CommonsChunkPlugin({
      name: 'common',
      chunks: ['a', 'b']
    }),
  ]
};

六.devServer

hot

默认:发现源代码被更新后会通过自动刷新整个页面来做到实时预览

inline

  • inline开启:构建完变化后的代码时通过代理客户端控制网页刷新
  • inline关闭:构建完变化后的代码时刷新 iframe 来实现实时预览

historyApiFallback

多个单页应用组成

historyApiFallback: {
  // 使用正则匹配命中路由
  rewrites: [
    //user 开头的都返回 user.html
    //game 开头的都返回 game.html
    // 其它的都返回 index.html
    { from: /^/user/, to: '/user.html' },
    { from: /^/game/, to: '/game.html' },
    { from: /./, to: '/index.html' },
  ]
}

contentBase

把项目根目录下的某目录设置成DevServer服务器的文件根目录

//把项目public目录,设置成DevServer服务器的文件根目录
devServer:{
  contentBase: path.join(__dirname, 'public')
}

headers

HTTP响应中注入一些HTTP响应头

devServer:{
  headers: {
    'X-foo':'bar'
  }
}

host

DevServer 服务监听的地址
host 的默认值是 127.0.0.1 即只有本地可以访问 DevServer 的 HTTP 服务

port

服务监听的端口

port: 9000

allowedHosts

配置一个白名单列表,在这个列表的才能正常返回

allowedHosts: [
  // 匹配单个域名
  'host.com',
  // host2.com 和所有的子域名 *.host2.com 都将匹配
  '.host2.com'
]

disableHostCheck

是否关闭用于 DNS 重绑定的 HTTP 请求的 HOST 检查

https

默认使用 HTTP 协议服务,它也能通过 HTTPS 协议服务,但要设置

devServer:{
  https: true
}

这样会自动生成https证书,如果想要自己的证书,需要这样配置

devServer:{
  https: {
    key: fs.readFileSync('path/to/server.key'),
    cert: fs.readFileSync('path/to/server.crt'),
    ca: fs.readFileSync('path/to/ca.pem')
  }
}

clientLogLevel

配置在客户端的日志等级,枚举类型.

  • none 不输出任何日志
  • error
  • warning
  • info 默认

compress

配置是否启用 gzip 压缩,默认为 false

open

为true时第一次构建完时自动用系统上默认的浏览器去打开项目,默认为 false

七.Target

target可以构建出针对不同环境的代码

  • target:'node' (针对 Node.js,使用 require 语句加载 Chunk 代码)
  • target:'web' 浏览器  (默认 ,所有代码都集中在一个文件里)
  • target:'async-node' (针对 Node.js,异步加载 Chunk 代码)
  • target:'webworker' (针对webworker)
  • target:'electron-main' (针对electron)
  • target:'electron-renderer' (针对electron)

八.Devtool

配置如何生成Source Map,默认值是 false不生成Source Map

module.export = {
  devtool: 'source-map'
}

九.Watch 和 WatchOptions

监听模式,它支持监听文件更新,默认是关闭的

module.export = {
  watch: true
}

使用 DevServer 时,监听模式默认是开启的
开启监听模式时,watchOptions 才有意义\

module.export = {
  watch: true,
  // 监听模式运行时的参数
  watchOptions: {
    // 不监听的文件或文件夹,支持正则匹配
    // 默认为空
    ignored: /node_modules/,
    // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
    // 默认为 300ms  
    aggregateTimeout: 300,
    // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
    // 默认每隔1000毫秒询问一次
    poll: 1000
  }
}