Webpack-基础概念

深入浅出Webpack学习笔记

基本概念

常用的构建工具

所有的构建工具所做的工做大致一样,都是把源代码翻译转换成可执行的代码,包括如下内容:

  • 代码转换:TypeScript转换成JavaScript,SCSS转换成CSS;
  • 文件优化:压缩JavaScript、CSS、图片等资源,利用一些优化手段,如摇树优化,移除无关代码;
  • 代码分割:提取多个页面的公共代码、提取首屏不需加载的代码让其异步加载,防止首次进入应用等待时间过长;
  • 模块合并:在一些模块化的项目中会有很多个模块和文件,需要构建工具把这些模块文件分类合并成一个文件;
  • 自动刷新:监听本地代码,自动构建加载,方便开发;
  • 代码校验:提交代码前进行代码规范检查;
  • 自动发布:更新完代码后,自动构建出线上代码,推送到线上或其他环境上;

Npm Script

Grunt

Gulp

Fis3

Webpack

https://www.webpackjs.com/
Webpack是一个模块化打包工具,专注于构建模块化项目,在Webpack眼里一切文件都是模块,通过Loader转换翻译文件,通过Plugin注入钩子,最后输出由多个模块组合成的文件。

之所以一切文件皆模块,如:JavaScript、CSS、SCSS以及图片等资源,在Webpack眼中都是模块,因为这样可以更好的理清描述各个模块之间的依赖关系,方便Webpack对模块进行打包组合,输出浏览器使用的静态资源。

简单使用:

module.exports = {
    // 定义入口文件
    entry: './index.js',
    // 定义打包输出文件
    output: {
        // 最终会把依赖的所有模块打包成一个bundle.js文件
        filename: './bundle.js'
    }
}

Webpack的优点

  • 专注于处理模块化项目,可以做到开箱即用,一步到位;
  • 通过Plugin进行扩展,完整好用又不失灵活;
  • 使用场景丰富,除了web端,其他场景也可以;
  • 社区活跃;
  • 开发体验好;

缺点是只能采用模块化开发项目。

Rollup

安装与使用

安装只需要一行命令,当然可以全局安装,但是不推荐。

npm install -D webpack

或者指定版本号:

npm install -D webpack@2.xxx

或者直接安装最新版:

npm install -D webpack@beta

注意:如果你使用的webpack版本较新,在webpack4.x测试下,你需要额外安装一依赖: npm i webpack-cli @webpack-cli/init。 可以参考webpack-cli

运行Webpack命令:

node_modules/.bin/webpack

或者通过配置npm script来运行:

"script": {
    "start": "webpack --config webpack.config.js"
}

具体如何使用?

前面有了基本使用方法,但是具体落实到代码上该怎么写?我们可以通过构建一个采用CommonJs模块化的简单Demo来理解。

建立如下文件:
|-- index.html // 入口文件
|-- show.js // js文件,里面我们随便写一个函数
|-- main.js // 入口文件
|-- package.json // npm 配置文件
|-- webpack.config.js // webpack 配置文件

index.html

index.html文件内容包含了一个script和一个id等于app的div。

<html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <div id="app"></div>
        <!-- 导入 Webpack 输出的 JavaScript 文件 -->
        <script src="./dist/bundle.js"></script>
    </body>
</html>

show.js

show.js文件定义了一个show函数,该方法将给页面中的div插入一段文本;同时,我们利用CommonJs规范,将该函数导出。

function show() {
    document.getElementById('app').innerText = 'hello world';
}
module.exports = show;

main.js

main.js文件将show.js引入,并执行show函数。

const show = require('./show.js');
show();

webpack.config.js

执行webpack构建执行命令的时候,会自动读取项目根目录下的webpack.config.js文件,所以我们新建该文件,并指明入口文件和打包输出文件。

const path = require('path');
module.exports = {
    entry: './main.js',
    output: {
        filename: './bundle.js',
        path: path.resolve(__dirnam, './dist') // 输出路径
    }
}

之所以使用CommonJs规范来导出webpack配置,是因为webpack运行在Node下,所以我们要使用CommonJs规范来描述一个如何构建的Object对象。

执行webpack构建命令后,在项目根目录下会多出一个dist文件夹,以及一个bundle.js文件。bundle.js依赖main.jsshow.js两个文件以及内置的webpackBootstrap启动函数,从入口文件main.js出发,识别出源码中模块化导入的语句,把入口文件所依赖的模块或文件递归的打包到一个文件中:bundle.js文件。

此时,直接打开index.html文件可以正常显示一段文案。

使用Loader

继续前面的内容,这次我们创建一个CSS文件: main.css
建立如下文件:
|-- index.html // 入口文件
|-- show.js // js文件,里面我们随便写一个函数
|-- main.js // 入口文件
|-- package.json // npm 配置文件
|-- webpack.config.js // webpack 配置文件
|-- main.css

文件中我们添加一段文本居中的样式:

#app {
    text-align: center;
}

然后,我们在main.js引入这个CSS文件:

// 引入css
require('./main.css');

const show = require('./show.js');
show();

编写工作做完后,我们自然的想到直接执行webpack构建命令,但是此时还不可以,因为Webpack原生仅支持解析JavaScript文件,如果需要
解析其他类型的文件,需要引入相应的Loader,这里,我们因为需要解析CSS,所以需要引入CSS Loader。

手动的去配置webpack.config.js文件:

const path = require('path');
module.exports = {
    entry: './main.js',
    output: {
        filename: './bundle.js',
        path: path.resolve(__dirname, './dist'),
    },
    module: {
        rules: [
            {
                // 用正则匹配css文件
                test: '/\.css$/',
                use: ['style-loader', 'css-loader?minimize'], // minimize:需要进行压缩
            }
        ]
    }
}

上面我们简单的配置了一个Loader规则。

Loader相当于一个翻译员,将某个文件源码翻译成可执行的代码。配置规则要求我们在rules数组中配置一个对象,指定test属性值来匹配那些文件需要翻译,通过use来指定需要使用哪些Loader,这里我们使用了style-loadercss-loader

需要注意的是,在配置use属性的时候:

  • use属性值是一个数组,数组中的每个元素为loader的名字,尤其要注意的是,Loader的执行顺序是由后到前;
  • 可以给Loader以URL querystring的形式传递参数,比如前面的css-loader?minimize,具体参可以参考所使用的Loader文档;

理解了Loader后,我们需要进行安装相应的Loader依赖:

npm install -D style-loader css-loader

所有准备工作做完后,我们执行构建命令:

npm start 或者 node_modules/.bin/webpack

然后再观察bundle.js文件,会发现代码更新了,并且CSS代码也被打包了进来,打开index.html,可以看到居中效果。

这里我们提一下,CSS之所可以写在JavaScript中,归功于刚才引入的style-lader,大概远离就是将CSS样式以字符串的形式存储到JavaScript对象中,然后在网页执行的时候,通过DOM操作动态的加入到页面中的<style>标签中。

当然,这样会导致页面加载时间变长,一定程度上需要我们再去优化处理,比如将CSS单独打包成一个文件,单独的输出,这种操作,我们可以通过Plugin来实现。Plugin也是Webpack的一个重要概念。

Tips:

use的配置中,给Loader传递参数除了刚才的写法,我们还可以传递一个对象来实现:

module.exports = {
    rules: [
        {
            test: '/\.css$/',
            use: ['style-loader', { loader: 'css-loader', options: { minimize: true } }],
        }
    ]
}

除了在webpack.config.js中配置Loader外,还可以在代码文件中直接引入相关Loader,比如刚才的场景就可以这么处理:

// main.js
requrie('style-loader!css-loader?minimize!.main.css');

这样就能指定对引入的main.css文件先进行css-loader在采用style-loder转换。

另外,前面我们提到了Loader的记载顺序是从后到前的,所以这里我们必须把css-loader放在后面,也就是先执行。因为css-loder是将css代码编译,而style-loader是将编译好的css加到页面中。

使用Plugin

Plugin是用来扩展Webpack功能的,给Webpack带来了很大的灵活性,通过在构建流程中注入钩子来实现。

继续前面的操作,我们这次需要优化一下,把main.css代码打包到单独的一个文件中。

我们需要在配置文件webpack.config.js文件中添加plugins属性,来配置Plugin。

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: 'main.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist'),
    },
    module: {
        rules: [
            {
                test: '/\.css$/',
                use: ExtractTextPlugin({
                    use: ['css-loader']
                })
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin({
            // 从.js中提取.css文件
            filename: `[name]_[contenthash:8].css`
        })
    ]
}

前面我们引入了新的插件,需要先安装:

npm install -D extract-text-webpack-plugin

然后我们执行构建命令,会发现dist目录下多出来一个.css结尾的CSS文件,bundle.js中也没有CSS代码了,然后我们手动将该CSS文件引入index.html就可以了。

通过上面的代码我们可以看到,我们可以通过配置plugins属性来配置,其值是一个数组,数组中的每一项是一个实例,并且在实例化一个对象的时候,我们可以通过构造函数传入这个组件支持的属性配置。

上面用到的extract-text-webpack-plugin就是一个插件,用来提取JavaScript中的CSS代码到一个单独的文件,filename属性指定了输出的文件名,[name]_[contenthash:8].cssname代表文件名,contenthash:8意思是根据文件内容算出8位hash值。

该插件的其他配置可以在官网上找到。

使用DevServer

到目前为止,我们也只是做了打包构建的工作,在正常的开发过程中,还需要实现下面的功能:

  • 代码自动构建,自动刷新,实现文件变化监听;
  • 提供HTTP服务;
  • 支持Source Map,方便调试。

上面提到的,Webpack原生支持1、3两点,对于提供HTTP服务,我们可以借助DevServe,是官方提供的一个开发工具。

DevServer会自动开启一个本地HTTP服务,同时会自动启动Webpack构建,并通过WebSocket协议接受Webpack的文件的实时变更,做到可以实时预览,方便我们开发。

安装与启动

安装DevServer:

npm install webpack-dev-server

启动DevServer

webpack-dev-server

启动成功后,我们可以在控制台看到一串输出:

Project is running at http://localhost:8080

此时,我们访问http://localhost:8080就会自动执行根目录下的index.html文件。

如果此时访问,我们会发现引入的bundle.js报404错误,是因为DevServer会把Webpack构建的文件保存在内存中,在要访问输出的文件时候,必须通过HTTP服务来访问,并且DevServer不会理会webpack.config.js配置里的output.path属性,所以我们需要访问http://localhost/bundle.js才可以。

修改index.html文件js引用路径:

<html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <div id="app"></div>
        <!-- 修改路径如下 -->
        <script src="./bundle.js"></script>
    </body>
</html>

实时预览

我们现在修改main.jsmain.cssshow.js文件中的任一一处,保存后,浏览器便会自动刷新,加载修改后的代码。

不过我们需要注意的是,通过DevServer启动的Webpack会自动开启文件监听,也就是这里的修改代码自动触发刷新页面的功能;而如果我们通过webpack来启动默认是不会开启监听模式的,只有我们显示的指明需要开启监听模式才可以。

开启监听模式: webpack --watch

DevServer会让Webpack在构建的过程中在JavaScript代码中注入一个代理客户端用于控制网页,并通过WebSocket协议进行通知,如果文件发生变化,会立刻告知刚才注入的代理客户端,代理客户端收到信息后,执行刷新网页操作。

但是如果我们修改index.html文件不会触发网页刷新操作,这是因为Webpack在启动时候会以配置中心的entry为口入去递归解析entry所以来的文件,只有entry本身和其所依赖的文件才会被添加到监听对象中;另外,index.html文件脱离了JavaScript模块化系统,所以Webpack监听不到。

模块热替换

模块热替换不同于前面的页面刷新,这里的模块热替换,可以在不刷新页面的情况下实现重新加载新的模块代码的效果,当有新的模块代码时候,会将新的替换掉老的,并重新执行一遍代码,从而做到不刷新页面,却可以实时预览的效果。

相比较来说,模块热更新在开发体验和效率上会略胜一筹。

模块热替换默认是关闭的,我们可以在启动DevServer的时候带上--hot参数来开启。

Source Map

通过指定devtool source-map参数来开启Source Map功能。

所谓的Source Map就是一份代码映射。在开过过程中,我们在浏览器看到的代码都是编译过的,所以没办法看到未编译的代码,很难去调试,代码可读性很差。

而Source Map可以将编译前的代码给映射出来,让我们可以在源码上调试。Webpack支持生成Source Map,只需要在启动的时候带上--devtool source-map参数。然后启动后,我们便可以在Chrome开发者工具下调试。

核心概念

  • Entry: 入口配置,Webpack构建的第一步将从Entry开始,可以抽象成输入;
  • Module:在Webpack里一切皆模块,一个模块对应一个文件。Webpack会从Entry入手,递归的找到所有的依赖模块;
  • Chunk:代码块,一个Chunk由多个模块组合而成,用于代码分割片段;
  • Loader:模块转换器,用于把模块中的内容按需求转换成新的内容,如ES6转换成ES5;
  • Plugin:扩展插件,在Webpack构建流程中特定时机注入扩展来改变逻辑和结果;
  • Output:输出结果,在Webpack经过前面一系列处理后返回的最终结果。

Webpack启动后会从Entry配置的Module开始,递归的解析其依赖的所有Module,每找到一个Module,会调用相应的Loader对其进行转换,对Module转换后,在解析当前Module所依赖的Module,同样会调用相应的Loader。这些Module会以Entry为单位分组,一个Entry和其依赖的所有Module都会打包成一个Chunk。最终Webpack会把Chunk转换成文件输出,整个构建流程中,Webpack会在特定时机执行Plugin定义的逻辑。

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

推荐阅读更多精彩内容