webpack二:webpack配置

webpack 是什么?

中文网站:https://webpack.docschina.org

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

安装

 npm install -D webpack webpack-cli
 npm install -D webpack@4 webpack-cli@3

webpack-cli : 提供 webpack 命令、工具

webpack : webpack 代码

使用

方法一:
./node_modules/.bin/webpack

// 查看版本
./node_modules/.bin/webpack -v
方法二:

编辑 package.jsonscripts 来简化输入

// package.json
{
    ...,
    "scripts": {
        "start": "webpack"  // scripts 中可以定位到 ./node_modules/.bin/ 目录下
    }
}
//scripts 中使用 test、start、restart、stop 命名的时候,可以在调用的时候省略 run,即直接 npm start
npm start
方法三:

通过 npx 也可以帮助我们定位命令到 ./node_modules/.bin/ 目录下

注:npm5.2+ 增加,如果没有,可以使用 npm i -g npx 来安装

npx webpack

打包模块

默认打包

跟上一个入口文件路径webpack 会从指定的入口文件开始分析所有需要依赖的文件,然后把打包成一个完整文件。

会使用 webpack 默认的配置对模块文件进行打包,并把打包后的文件输出到默认创建的 ./dist 目录下,打包后的文件名称默认为 main.js

npx webpack ./js/index.js

打包配置

webpack 命令在运行的时候,默认会读取运行命令所在的目录下的 webpack.config.js 文件。

也可以通过 —config 选项来指定配置文件路径:

webpack --config ./configs/my_webpack.config.js
module.exports = {
  ...   //配置项
}

mode

模式 : "production" | "development" | "none"

不同的模式下,webpack设置了不同的默认配置,打包的时候进行一些对应的优化。

module.exports = {
  mode: 'production'
}

entry

指定打包⼊口⽂文件,有三种不同的形式

一个入口、输出一个打包文件

module.exports = {
  entry: './src/index.js'
}

多个入口、输出一个打包文件

module.exports = {
  entry: [
    './src/index1.js',
    './src/index2.js',
  ]
}

多个入口、输出多打包文件,文件名为对象的key值。

module.exports = {
  entry: {
    'index1': "./src/index1.js",
    'index2': "./src/index2.js"
  }
}

output

默认放在dist目录下,可以通过output修改打包后的文件位置

module.exports = {
  ...,
  output: {
    //path必须是绝对路径,__dirname当前目录
    path: path.resolve(__dirname, "./build"),
    filename: "bundle.js", //单文件出口可以指定固定文件名
    //多文件出口,name从entry的key取
    //filename: "[name].js"
    }
}

WebpackDevServer

启动服务以后,webpack 不在会把打包后的文件生成到硬盘真实目录中,而是直接存在了内存中(同时虚拟了一个目录路径)

npm install --save-dev webpack-dev-server

启动命令:

npx webpack-dev-server

或者,package.json 中添加 scripts

...,
"scripts": {
  "server": "webpack-dev-server"
}

webpack五可以使用webpack server

...,
"scripts": {
  "server": "webpack server"
}

修改 webpack.config.js

module.exports = {
  ...,
  devServer: {
    // 自动开启浏览器
    open: true,
    // 端口
    port: 8081
    }
}

Proxy

后端代码
const Koa = require('koa');
const KoaRouter = require('koa-router');
const app = new Koa();
const router = new KoaRouter();

router.get('/info', async ctx => {
    ctx.body = {
        username: 'zMouse',
    }
})
app.use( router.routes() );
app.listen(8787);
前端代码
//会产生跨域请求
axios({
  url: 'http://localhost:8787/info'
}).then(res => {
  console.log('res',res.data);
})
//webpack配置后代码
axios({
  url: 'http://localhost:8787/api/info'
}).then(res => {
  console.log('res',res.data);
})
module.exports = {
    ...,
    devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:8787',
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
}

Hot Module Replacement

实现不刷新页面,只更新变化的部分

开启 HMR 以后,当代码发生变化,webpack 即会进行编译,并通过 websocket 通知客户端(浏览器),我们需要监听处理来自 webpack 的通知,然后通过 HMR 提供的 API 来完成我们的局部更新逻辑

module.exports = {
  ...,
  devServer: {
    // 开启热更新,如果没写moudle.hot会回退为`live reload`
    hot:true,
    // 即使 HMR 不生效,也不去刷新整个页面(选择开启)
    hotOnly:true,
    }
}

一个js代码热更新示例:

当 fn1.js 模块代码发生变化的时候,把最新的 fn1 函数绑定到 box1.onclick 上

//fn1.js
export default function() {
    console.log('start1!');
}
import fn1 from './fn1.js';
box1.onclick = fn1;

if (module.hot) {//如果开启 HMR
    module.hot.accept('./fn1.js', function() {
      // 更新逻辑
      box1.onclick = fn1;
    })
}

HMR 以模块为单位,当模块代码发生修改的时候,通知客户端进行对应的更新,而客户端则根据具体的模块来更新我们的页面逻辑

当前一些常用的更新逻辑都有了现成的插件

css热更新

样式热更新,style-loader 中就已经集成实现

react 热更新

react 脚手架中已经集成

https://github.com/gaearon/react-hot-loader

vue 热更新

vue 脚手架中已经集成

https://github.com/vuejs/vue-loader

sourceMap

https://webpack.docschina.org/configuration/devtool/#devtool

webpack 会打包合并甚至是压缩混淆代码,所生成的代码运行在浏览器时不利于我们的调试和定位错误,

sourceMap是 记录了编译后代码与源代码的映射关系的文件

通过 webpackdevtool 选项来开启 sourceMap

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  ...
}

编译后会为每一个编译文件生成一个对应的 .map 文件,同时在编译文件中添加一段对应的注释,和对应的 map 文件关联

...
//# sourceMappingURL=main.js.map
...
/*# sourceMappingURL=main.css.map*/

配置推荐:

devtool:"eval-cheap-module-source-map",// 开发环境
devtool:"source-map"||false,   // 生产环境

Code Spliting

将代码分割到多个不同的bundle(打包后)文件中,可以通过按需加载等方式对资源进行加载

入口起点

通过设置多个入口文件的方式实现最简单的代码分割

entry: {
  index: "./src/index.js",
  list: "./src/list.js",
},
output: {
  path: resolve(__dirname, "../dist"),
  // 多入口文件的filename不能写死名称,需要通过[name]配置
  filename: "js/[name].js",
}

防止重复

如果只按上述配置,若index和list都使用了axios,都会把axios代码打包其中。

可以通过设置dependOn配置多个模块之间的共享文件,将共享的axios代码提取成一个单独文件。

entry: {
  index: {
    import: "./src/index.js",
    dependOn: "axios",
  },
  list: {
    import: "./src/list.js",
    dependOn: "axios",
  },
  axios: "axios",
},
//需要设置runtimeChunk为single,在所有生成 chunk 之间共享运行时文件,否则axios会生成两个实例
optimization: {
  runtimeChunk: 'single',
},

SplitChunksPlugin

将公共的依赖模块提取到已有的入口chunk文件或新的chunk文件当中

entry: {
  index: "./src/index.js",
  list: "./src/list.js",
},
optimization: {
  splitChunks: {
    // async表示只从异步加载得模块(动态加载import())里面进行拆分
    // initial表示只从入口模块进行拆分
    // all表示以上两者都包括
    chunks: "all",
  },
}

动态导入

通过import()动态导入模块,可以通过内联注释对chunk进行一些配置

模块方法 | webpack 中文文档 (docschina.org)

import(/* webpackChunkName: 'data', webpackPreload: true*/ './data')
    .then(data => {
        console.log(data);
    })

Prefetch/Preload

通过内联注释webpackPrefetchwebpackPreload两种资源提示告知浏览器对资源进行不同的加载处理

const data = import(/* webpackChunkName: 'data', webpackPreload: true */ './data.js')
const data = import(/* webpackChunkName: 'data', webpackPrefetch: true */ './data.js')

与 prefetch 指令相比,preload 指令有许多不同之处:

preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。

外部扩展

通过externals配置在输出的bundle中排除某些依赖,这些依赖将会在用户环境中所包含。

externals: 
  lodash: '_'
},

tree shaking

将上下文中的dead-code移除,就像摇树上的枯叶使其掉落一样

optimization: {
  usedExports: true,
}

配置合并

npm install webpack-merge -D
const merge = require("webpack-merge") 
const commonConfig = require("./webpack.common.js") 
const devConfig = { ... }
module.exports = merge(commonConfig,devConfig)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343