Webpack实践

webpack介绍和安装

webpack介绍

为什么需要webpack

由于前端之前js、css、图片文件需要单独进行压缩和打包,这样团队人员处理很繁琐,然后 Instagram 团队就想让这些工作自动化,然后webpack应运而生。

是什么

webpack是一个模块打包器(module bundler),webpack视HTML,JS,CSS,图片等文件都是一种 资源 ,每个资源文件都是一个模块(module)文件,webpack就是根据每个模块文件之间的依赖关系将所有的模块打包(bundle)起来。

有什么用

  • 对 CommonJS 、 AMD 、ES6的语法做了兼容
  • 对js、css、图片等资源文件都支持打包(适合团队化开发)
  • 比方你写一个js文件,另外一个人也写一个js文件,需要合并很麻烦,现在交给webpack合并很简单
  • 有独立的配置文件webpack.config.js
  • 可以将代码切割成不同的chunk,实现按需加载,降低了初始化时间
  • 具有强大的Plugin(插件)接口,大多是内部插件,使用起来比较灵活

webpack使用的名词

webpack的核心概念分为 入口(Entry)加载器(Loader)插件(Plugins)出口(Output)

  • 入口(Entry):入口起点告诉 webpack 从哪里开始,并根据依赖关系图确定需要打包的文件内容
  • 加载器(Loader):webpack 将所有的资源(css, js, image 等)都看做模块,但是 webpack 能处理的只是 JavaScript,因此,需要存在一个能将其他资源转换为模块,让 webpack 能将其加入依赖树中的东西,它就是 loader。loader用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。如less-loader,css-loader,sass-loader,url-loader,file-loader
  • 插件(Plugins):loader 只能针对某种特定类型的文件进行处理,而 plugin 的功能则更为强大。在 plugin 中能够介入到整个 webpack 编译的生命周期,Plugins用于解决 loader 无法实现的其他事情,也就是说loader是预处理文件,那plugin 就是后处理文件。如:代码压缩从而减小文件体积,热更新(浏览器实时显示)
  • 出口(Output):指定打包后文件的路径目录和文件名

与grunt/gulp的对比

grunt/gulp的核心是Task,我们可以配置一系列的task(任务),并且定义task要处理的事务(例如ES6、ts转化,图片压缩,scss转成css)之后让grunt/gulp来依次执行这些task,而且让整个流程自动化。所以grunt/gulp也被称为前端自动化任务管理工具。

我们来看一个gulp的task下面的task就是将src下面的所有js文件转成ES5的语法。并且最终输出到dist文件夹中。

const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('js',()=>
		gulp.src('src/*.js)
			.pipe(babel({
                presets:['es2015]
                
			}))
			.pipe(gulp.dest('dist'))
);

什么时候用grunt/gulp呢?

如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。只需要进行简单的合并、压缩,就使用grunt/gulp即可。但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。

所以,grunt/gulp和webpack有什么不同呢?

grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。

webpack使用环境准备

安装Node.js

webpack基于node.js运行,首先需要安装node.js

为什么会有node.js?

传统意义上的 JavaScript 运行在浏览器上,Chrome 使用的 JavaScript 引擎是 V8,Node.js 是一个运行在服务端的框架,它的底层就使用了 V8 引擎,这样就可以使用javascript去编写一些服务端的程序,这样也就实现了用 javaScript去开发 Apache + PHP 以及 Java Servlet所开发的服务端功能,这样做的好处就是前端和后端都采用 javascript,即开发一份js程序即可以运行在前端也可以运行的服务端,这样比一个应用使用多种语言在开发效率上 要高,不过node.js属于新兴产品,一些公司也在尝试使用node.js完成一些业务领域,node.js基于V8引擎,基于事件驱动机制,在特定领域性能出色,比如用node.js实现消息推送、状态监控等的业务功能非常合适。

  1. 下载对应你系统的Node.js版本: https://nodejs.org/en/download/ 推荐下载LTS版本

  2. 选安装目录进行安装,默认即可,安装完成检查PATH环境变量是否设置了node.js的路径。

  3. 在命令提示符下输入命令 node -v测试,会显示当前node的版本

    C:\Users\L15096000421>node -v
    v12.13.0
    

安装NPM

1、自动安装NPM

npm全称Node Package Manager,他是node包管理和分发的工具,使用NPM可以对应用的依赖进行管理,NPM的功能和服务端项目构建工具maven差不多,我们通过npm 可以很方便地下载js库,打包js文件。 node.js已经集成了npm工具,在命令提示符输入 npm -v 可查看当前npm版本

C:\Users\L15096000421>npm -v
6.13.2

2、设置包路径

包路径就是npm从远程下载的js包所存放的路径;

使用 npm config ls 查询NPM管理包路径(NPM下载的依赖包所存放的路径)

npm@6.13.2 C:\Users\L15096000421\AppData\Roaming\npm\node_modules\npm

NPM默认的管理包路径在C:/用户/[用户名]/AppData/Roming/npm/node_meodules,为了方便对依赖包管理,我们可以将管理包的路径设置在单独的地方,本教程将安装目录设置在node.js的目录下,创建npm_modulesnpm_cache,执行下边的命令:

npm config set prefifix "C:\Program Files\nodejs\npm_modules" 
npm config set cache "c:\Program Files\nodejs\npm_cache" 

此时再使用 npm config ls 查询NPM管理包路径发现路径已更改

3、安装cnpm

npm默认会去国外的镜像去下载js包,在开发中通常我们使用国内镜像,这里使用淘宝镜像;

输入命令,进行全局安装淘宝镜像。

npm install -g cnpm --registry=https://registry.npm.taobao.org

安装后,我们可以使用cnpm -v 命令来查看cnpm的版本

C:\Users\L15096000421>cnpm -v
cnpm@6.1.1 (C:\Users\L15096000421\AppData\Roaming\npm\node_modules\cnpm\lib\parse_argv.js)
npm@6.14.5 (C:\Users\L15096000421\AppData\Roaming\npm\node_modules\cnpm\node_modules\npm\lib\npm.js)
node@12.13.0 (D:\kaifagongju\node\node install\node.exe)
npminstall@3.27.0 (C:\Users\L15096000421\AppData\Roaming\npm\node_modules\cnpm\node_modules\npminstall\lib\index.js)
prefix=C:\Users\L15096000421\AppData\Roaming\npm
win32 x64 10.0.18362
registry=https://r.npm.taobao.org

nrm ls 查看镜像是否已经指向taobao

C:\Users\L15096000421>nrm ls

* npm -------- https://registry.npmjs.org/
  yarn ------- https://registry.yarnpkg.com/
  cnpm ------- http://r.cnpmjs.org/
  taobao ----- https://registry.npm.taobao.org/
  nj --------- https://registry.nodejitsu.com/
  npmMirror -- https://skimdb.npmjs.com/registry/
  edunpm ----- http://registry.enpmjs.org/

使nrm use XXX切换镜像

如果nrm没有安装则需要进行全局安装:cnpm install -g nrm

安装webpack

webpack安装分为本地安装和全局安装:

本地安装: 仅将webpack安装在当前项目的node_modules目录中,仅对当前项目有效。

全局安装: 将webpack安装在本机,对所有项目有效,全局安装会锁定一个webpack版本,该版本可能不适用某个 项目。全局安装需要添加 -g 参数

1、本地安装

只在我的项目中使用webpack,需要进行本地安装,因为项目和项目所用的webpack的版本不一样。本地安装就会 将webpack的js包下载到项目下的npm_modeuls目录下。

npm install --save-dev webpack 或 cnpm install --save-dev webpack
npm install --save-dev webpack-cli (4.0以后的版本需要安装webpack-cli)

--save-dev可以使用-D参数代替

--save :将配置信息保存到package.json中,同时 --save :也是项目生产环境,项目发布之后还依赖的东西,保存在dependencies

例如:如果你用了 jQuery,由于发布之后还是依赖jQuery,所以是dependencies

--save-dev :是项目开发环境依赖的东西,保存在devDependencies中

例如:写 ES6 代码,如果你想编译成 ES5 发布那么 babel 就是devDependencies

2、全局安装加-g

全局安装就将webpackjs包下载到npm的包路径下。

npm install webpack -g 或 cnpm install webpack -g

3、安装webpack指定的版本,不指定代表安装最新的

本地安装: cnpm install --save-dev webpack@3.6.0
全局安装:npm install webpack@3.6.0 -g或 cnpm install webpack@3.6.0 -g

4、测试:在cmd状态输入webpack -v,出现如下提示说明 webpack安装成功

E:\webpacktest>webpack -v
4.43.0

为什么全局安装后,还需要局部安装呢?

在终端直接执行webpack命令,使用的全局安装的webpack;当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack

webpack配置使用

测试环境

  • WebStorm 2020.1.2 x64
  • node — v12.13.0
  • webpack — 4.43.0
  • npm — 6.13.2

搭建项目结构

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="text" id="first">
    <input type="button" id="btn" value="+">
    <input type="text" id="two">
    <input type="button" id="btn" value="=">
    <input type="text" id="res">
    <script src="./dist/js/bulid.js"></script>
</body>
</html>

js

moudle1.js

function sum(x,y){
    return x + y;
}
// 导出 sum 函数
module.exports = sum;

main.js

// 1、获取index.html中的dom对象

var first = document.getElementById('first');
var btn = document.getElementById('btn');
var two = document.getElementById('two');
var res = document.getElementById('res');
	btn.onclick = function(){
		var firstValue = parseFloat(first.value);
		var twoValue = parseFloat(two.value);
		//2、获取 module1.js中的 sum函数
		//http://www.ruanyifeng.com/blog/2015/05/require.html
		var sum = require('./module1.js');
		res.value = sum(firstValue,twoValue);
}

webpack.config.js(配置文件)

const path = require('path');  // 首先要引入node.js中path 模块,用于处理文件与目录的路径

// const 命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错
// 常量存储的是一个不可以变化的变量。

module.exports = {
    entry:'./src/./js/main.js', // 指定入口文件:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可
    output:{ //出口:通常是一个对象,里面至少包含两个重要属性,path和filename
        path: path.resolve(__dirname, './dist'), // 指定出口文件的路径目录
        filename: 'bulid.js' // 制定出口文件的名称
    }
}

初始化项目

进入到根目录webpacktest,使用 cnpm init 指令创建项目描述文件 package.json文件。

cnpm init 

命令行里会以交互的形式让你填一些项目的介绍信息,依次介绍如下:(不知道怎么填的直接回车、回车…)

  • name 项目名称,不填则默认更目录
  • version 项目的版本号,不填则默认1.0.0
  • description 项目的描述信息
  • entry point 项目的入口文件,不填则默认index.js,后面可以修改
  • test command 项目启动时脚本命令
  • git repository 如果你有 Git 地址,可以将这个项目放到你的 Git 仓库里
  • keywords 关键词
  • author 作者叫啥
  • license 项目要发行的时候需要的证书,平时玩玩忽略它

测试

根目录webpacktest新建app.js

console.log('我很帅')

执行命令node app.js

证明了node是提供了JavaScript的运行环境

当前项目安装Webpack依赖包

只在我的项目中使用webpack,需要进行本地安装,因为项目和项目所用的webpack的版本不一样。本地安装就会 将webpack的js包下载到项目下的npm_modeuls目录下。

cnpm install --save-dev webpack
cnpm install --save-dev webpack-cli (4.0以后的版本需要安装webpack-cli)

--save-dev可以使用-D参数代替

--save :将配置信息保存到package.json中,同时 --save :也是项目生产环境,项目发布之后还依赖的东西,保存在dependencies

例如:如果你用了 jQuery,由于发布之后还是依赖jQuery,所以是dependencies

--save-dev :是项目开发环境依赖的东西,保存在devDependencies中

例如:写 ES6 代码,如果你想编译成 ES5 发布那么 babel 就是devDependencies

打包测试

loader的使用

在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。
但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。对于webpack本身的能力来说,对于这些转化是不支持的。那怎么办呢?给webpack扩展对应的loader就可以啦。

loader使用过程:

  • 通过npm安装需要使用的loader
  • 在webpack.config.js中的modules关键字下进行配置

实现CSS打包

  1. 本地安装所需的加载器
cnpm install css-loader style-loader --save-dev

2.在src—>css目录中新建main.css

#first{
    border: 1px solid red;
}

3.在webpack.config.js中配置相关的loader

const path = require('path');  // 首先要引入node.js中path 模块,用于处理文件与目录的路径

// const 命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错
// 常量存储的是一个不可以变化的变量。
// 
module.exports = {
    entry:'./src/./js/main.js', // 指定入口文件
    output:{
        path: path.resolve(__dirname, './dist/js'), // 指定出口文件的路径目录
        filename: 'bulid.js' // 制定出口文件的名称
    },
    module:{
        rules:[
        // 在webpack2中,loaders 被替换成了 rules 其实就是loader的规则
            {
                test: /\.css$/,
                use: [ 'style-loader', 'css-loader' ]
                // test 说明了当前 loader 能处理那些类型的文件
                // use 则指定了 loader 的类型。
                // 注意:这次因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。
            }
        ]
    }
}

4.在main.js中获取css目录中的main.css文件,webpack会根据入口文件所有依赖的文件进行打包

// 1、获取index.html中的dom对象
var first = document.getElementById('first');
var btn = document.getElementById('btn');
var two = document.getElementById('two');
var res = document.getElementById('res');
    btn.onclick = function(){
        var firstValue = parseFloat(first.value);
        var twoValue = parseFloat(two.value);
        //2、获取 module1.js中的 sum函数
        //http://www.ruanyifeng.com/blog/2015/05/require.html
        var sum = require('./module1.js');
        res.value = sum(firstValue,twoValue);
    }

    // 3、获取css目录中的main.css文件
    require('../css/main.css');

在终端中输入 webpack命令进行css文件打包

package.json中定义启动

每次执行都敲这么一长串有没有觉得不方便呢?我们可以在package.jsonscripts中定义自己的执行脚本。

package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。

  • 首先,会寻找本地的node_modules/.bin路径中对应的命令。
  • 如果没有找到,会去全局的环境变量中寻找。
  • 如何执行我们的build指令呢?cnpm run build

实现SCSS打包

本地安装对应的loader

cnpm install sass-loader node-sass webpack --save-dev

在src目录中新建 sass目录–> scss1.scss

// scss1.scss文件
$color:purple;
#two{
    border:1px solid $color; 
}

webpack.config.js中配置相关的loader,**注意:**在 rules数组中配置

{
    test: /\.scss$/,
    // 注意 是sass-loader,不是 scss-loader
    //安装:cnpm install sass-loader css-loader style-loader node-sass webpack --save-dev
    use: [ 'style-loader', 'css-loader', 'sass-loader' ]
}

在js目录中 main.js里面引入 scss1.scss

// 4、获取sass目录中的scss1.scss文件
require('../sass/scss1.scss');

在终端中输入 webpack命令进行scss文件打包

实现Less打包

本地安装对应的loader加载器

cnpm install less less-loader css-loader style-loader  webpack --save-dev

在在src目录中新建less目录–> less1.less

@color:blue;
#res{
    border:1px dashed blue;
}

webpack.config.js中配置相关的loader,**注意:**在 rules数组中配置

// 实现 less 打包
{
    test: /\.less$/,
    use: [ 'style-loader', 'css-loader', 'less-loader' ]
}

在js目录中 main.js里面引入 less1.less文件

// 5、获取less目录中的less1.less文件
require('../less/less1.less');

在终端中输入 webpack命令进行scss文件打包

实现打包url资源(图片、gif、图标等)功能

  1. 在src 目录中 新建imgs目录,放入两张不同大小的图片,一张较小的图片vue1.png’(小于8kb),一张较大的图片vue_cover_logo.png(大于8kb)

  2. 在index.html中新增 <div id="bg1"></div> <div id="bg2"></div>

  3. 在css文件夹新增–>icons-extra.css

    #bg1{
        width: 1500px;
        height: 500px;
        background: url('../imgs/vue1.png') no-repeat;
    }
    
    #bg2{
        width: 500px;
        height: 500px;
        background: url('../imgs/vue_cover_logo.png') no-repeat;
    }
    

    如果我们现在直接打包,会出现如下问题

  1. 图片处理,我们使用url-loader来处理,依然先本地安装url-loader加载器
cnpm install url-loader  --save-dev
  1. 在webpack.config.js中配置相关的loader

      // 实现 url 资源打包
                {
                    // 图片和字体文件使用 url-loader 来处理
                    test: /\.(png|jpg|gif|ttf|eot|woff|woff2|svg)$/,
                    use: [
                        {
                            loader: 'url-loader',
                            // options 为可以配置的选项
                            options: {
                                limit: 8192
                                // limit=8192表示将所有小于8kb的图片都转为base64形式(为什么                               呢?因为一个很小的图片,不值当的去发送一个请求,减少请求次                               数。)
                                // (其实应该说超过8kb的才使用 file-loader 来映射到文件,否                              则转为dataurl形式)
                            }
                        }
                  ]
                   //保证输出的图片名称与之前命名的图片名称保持一致(目前只是支持这样的写法,                   webpack3 没有响应的options进行配置)
                  use:'url-loader?limit=8192&name=[path][name].[ext]' 
              } 
    

    在main.js中引入css目录中icons-extra.css的文件

    // 6、获取src目录中的mui目录中icons-extra.css的文件
    require('../mui/css/icons-extra.css');
    

    在终端中输入 npm run build命令进行icons-extra.css文件打包,而仔细观察,你会发现背景图是通过base64显示出来的;

    OK,这也是limit属性的作用,当图片小于8kb时,对图片进行base64编码

那么问题来了,如果大于8kb呢?我们将background的图片改成vue_cover_logo.png

这次因为大于8kb的图片,会通过file-loader进行处理,但是我们的项目中并没有file-loader

所以,我们需要安装file-loader

cnpm install file-loader --save-dev

再次打包,就会发现dist文件夹下多了一个图片文件

我们发现webpack自动帮助我们生成一个非常长的名字;

这是一个32位hash值,目的是防止名字重复。但是,真实开发中,我们可能对打包的图片名字有一定的要求。
比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时也要防止重复 ,所以,我们可以在options中添加上如下选项:

  • img:文件要打包到的文件夹
  • name:获取图片原来的名字,放在该位置
  • hash:8:为了防止图片名称冲突,依然使用hash,但是我们只保留8位
  • ext:使用图片原来的扩展名

但是,我们发现图片并没有显示出来,这是因为图片使用的路径不正确

默认情况下,webpack会将生成的路径直接返回给使用者。 但是,我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/

百度查配置文件

module.exports = {
    // 入口文件路径,__dirname是根目录
    entry: __dirname + '/src/main.js',
    // 打包生成文件
    output: {
        path: __dirname + '/output',
        filename: 'main.js'
    },
 
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.jpeg$/,
                use: 'url-loader?limit=1024&name=[path][name].[ext]&outputPath=img/&publicPath=output/',
            }
        ]
    }
  }
}

这里涉及到了4个参数:limit、name、outputPath、publicPath。

  • limit参数就是告诉url-loader,在文件小于多少个字节时,将文件编码并返回DataURL。

  • name表示输出的文件名规则,如果不添加这个参数,输出的就是默认值:文件哈希。加上[path]表示输出文件的相对路径与当前文件相对路径相同,加上[name].[ext]则表示输出文件的名字和扩展名与当前相同。加上[path]这个参数后,打包后文件中引用文件的路径也会加上这个相对路径。

    不加[path],可能引图片路径会找不到404

    加[path]打包后文件中引用文件的路径也会加上这个相对路径。

  • outputPath表示输出文件路径前缀。图片经过url-loader打包都会打包到指定的输出文件夹下。但是我们可以指定图片在输出文件夹下的路径。比如outputPath=img/,图片被打包时,就会在输出文件夹下新建(如果没有)一个名为img的文件夹,把图片放到里面。

  • publicPath表示打包文件中引用文件的路径前缀,如果你的图片存放在CDN上,那么你上线时可以加上这个参数,值为CDN地址,这样就可以让项目上线后的资源引用路径指向CDN了。

ES6转换为ES5语法

安装loader加载器

cnpm install --save-dev babel-loader babel-core babel-preset-env

  • babel-core如果某些代码需要调用Babel的API进行转码,就要使用babel-core模块
  • babel-preset-env通过根据您的目标浏览器或运行时环境自动确定您需要的Babel插件

配置

{
        test: /\.js$/,
        exclude: /(node_modules)/,  // exclude 排除的意思,把 node_modules文件夹排除编译之外
        use: {
            loader: 'babel-loader',
            options: {
            // presets 预设列表(一组插件)加载和使用
            presets: ['env'],
            plugins: ['transform-runtime'] // 加载和使用的插件列表
            }
		}
}

把一些代码改成ES6 语法的写法

//moudule1.js

function sum(x,y){
    return x + y;
}
// 导出 sum 函数
// module.exports = sum;
    // 改成ES6 的写法语法
export default{
    sum
}

main.js

 var first = document.getElementById('first');
    var btn1 = document.getElementById('btn1');
    var two = document.getElementById('two');
    var res = document.getElementById('res');
    console.log(1);
    btn1.onclick = function() {
        var firstValue = parseFloat(first.value);
        var twoValue = parseFloat(two.value);
        //2、获取 module1.js中的 sum函数
        //http://www.ruanyifeng.com/blog/2015/05/require.html
        console.log(2);
      
        /* var sum = require('./module1.js');
         res.value = sum(firstValue,twoValue);*/
        res.value = sumObj.sum(firstValue, twoValue);
    }

    // 3、获取css目录中的main.css文件
    // require('../css/main.css');

    //  把步骤3 改为 ES6写法,引入css目录中的main.css文件
    import '../css/main.css';

    // 4、获取sass目录中的scss1.scss文件
    require('../sass/scss1.scss');

    // 5、获取less目录中的less1.less文件
    require('../less/less1.less');

    // 6、获取src目录中的mui目录中icons-extra.css的文件
    require('../mui/css/icons-extra.css');

    // 把 var sum = require('./module1.js'); 写成 ES6语法
    import sumObj from './module1.js'


plugin的使用

webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。

loader和plugin区别:

  • loader主要用于转换某些类型的模块,它是一个转换器。
  • plugin是插件,它是对webpack本身的扩展,是一个扩展器。

plugin的使用过程:

  • 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
  • 在webpack.config.js中的plugins中配置插件

添加版权的Plugin

该插件名字叫BannerPlugin,属于webpack自带的插件。

plugins:[
    new webpakc.BannerPlugin('最终版权归zysheep所有')
]

打包html的plugin

目前,我们的index.html文件是存放在项目的根目录下的。我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。所以,我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件

HtmlWebpackPlugin插件可以为我们做这些事情:

  • 自动生成一个index.html文件(可以指定模板来生成)
  • 将打包的js文件,自动通过script标签插入到body中

安装HtmlWebpackPlugin插件

cnpm install html-webpack-plugin --save-dev

使用插件,修改webpack.config.js文件中plugins部分的内容如下:

//引入
const HtmlWebpackPlugin = require('html-webpack-plugin');

plugins: [
    new HtmlWebpackPlugin({
    	template: "index.html"
    })
]

这里的template表示根据什么模板来生成index.html,另外,我们需要删除之前在output中添加的publicPath属性,否则插入的script标签中的src可能会有问题

js压缩的Plugin

在项目发布之前,我们必然需要对js等文件进行压缩处理。这里,我们就对打包的js文件进行压缩我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和CLI2保持一致

cnpm install uglifyjs-webpack-plugin@1.1.1 --save-dev

修改webpack.config.js文件,使用插件:

new

查看打包后的bunlde.js文件,是已经被压缩过了。

Webpack-dev-server结合后端服务器的热替换配置

webpack-dev-server提供了一个简单的 web 服务器,并且能够实现热加载 并且自动刷新浏览器,html-webpack-plugin插件根据html模板在内存生成html文件,它的工作原理是根据模板文件在内存中生成一个index.html文件。这样的话,即使我们的目录中没有了相关js等文件,还能够加载出来,这样能够提高我们页面运行速度。

安装 webpack-dev-server 插件和html-webpack-plugin插件

cnpm install webpack-dev-server --save-dev 

cnpm install --save-dev html-webpack-plugin

在webpack.config.js中配置webpack-dev-server

注意:与module同级

   devServer: {     
        // 为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
        contentBase: path.join(__dirname, "dist"),
        compress: true,
        // 当它被设置为true的时候对所有的服务器资源采用gzip压缩
        // 对JS,CSS资源的压缩率很高,可以极大得提高文件传输的速率,从而提升web性能
        port: 9000, // 如果想要改端口,可以通过 port更改
        hot: true, // 启用 webpack 的模块热替换特性()
        inline: true, // 实现实时重载(实现自动刷新功能)默认情况下是 true。
        host: "localhost" // 如果你希望服务器外部可访问,指定使用一个 host。默认是 localhost(也就是你可以不写这个host这个配置属性)。
    }

在package.json中配置script

 "start": "webpack-dev-server --open"
 
 //"start": "webpack-dev-server --open --port 8080 --hot --inline" 
 // 如果在这里配置了,就不用在webpack.config.js中配置devServer属性了。
 --inline:自动刷新
 --hot:热加载 
 --port:指定端口 
 --open:自动在默认浏览器打开
 --host:可以指定服务器的 ip,不指定则为127.0.0.1,如果对外发布则填写公网ip地址

在webpack.config.js中配置html-webpack-plugin

需要引入html-webpack-plugin 插件与webpack,并且与module同级

// 引入html-webpack-plugin 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');


plugins: [
        new HtmlWebpackPlugin({
            title: '首页', // 用于生成的HTML文档的标题
            filename: 'index.html', //写入HTML的文件。默认为index.html。也可以指定一个子目录(例如:)assets/admin.html
            template: 'index.html' // Webpack需要模板的路径
        }),
        new webpack.HotModuleReplacementPlugin() // 需要结合 启用热替换模块(Hot Module Replacement),也被称为 HMR
    ]

此时package.json的文件内容如下:

{
  "name": "webpacktest",
  "version": "1.0.0",
  "description": "node测试",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open"
  },
  "author": "zysheep",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^3.5.3",
    "file-loader": "^6.0.0",
    "html-webpack-plugin": "^4.3.0",
    "less": "^3.11.3",
    "less-loader": "^6.1.1",
    "node-sass": "^4.14.1",
    "sass-loader": "^8.0.2",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.11.0"
  }
}

此时webpack.config.js的文件内容如下:

const path = require('path');  // 首先要引入node.js中path 模块,用于处理文件与目录的路径

// const 命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错
// 常量存储的是一个不可以变化的变量。

// 引入html-webpack-plugin 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

module.exports = {
    entry: './src/./js/main.js', // 指定入口文件
    output: {
        path: path.resolve(__dirname, './dist'), // 指定出口文件的路径目录
        filename: 'bulid.js' // 制定出口文件的名称
    },
	 module:{
        rules:[
        // 在webpack2中,loaders 被替换成了 rules 其实就是loader的规则
            //安装 cnpm install css-loader style-loader --save-dev
            {
                test: /\.css$/,
                use: [ 'style-loader', 'css-loader' ]
                // test 说明了当前 loader 能处理那些类型的文件
                // use 则指定了 loader 的类型。
                // 注意:数组中的loader不能省略扩展名
            },
			{
                   test: /\.scss$/,
                   // 注意 是sass-loader,不是 scss-loader
                    //安装:cnpm install sass-loader css-loader style-loader node-sass webpack --save-dev
                   use: [ 'style-loader', 'css-loader', 'sass-loader' ]
            },
			  // 实现 less 打包  安装:cnpm install less less-loader css-loader style-loader  webpack --save-dev
            {
                test: /\.less$/,
                use: [ 'style-loader', 'css-loader', 'less-loader' ]
            },
            // 实现 url 资源打包
            {
                // 图片和字体文件使用 url-loader来处理
                //安装:cnpm install url-loader file-loader --save-dev
                test: /\.(png|jpg|gif|ttf|eot|woff|woff2|svg)$/,
                use: [
                    {
                        loader: 'url-loader',
                        // options 为可以配置的选项
                        options: {
                            limit: 8192
                            // limit=8192表示将所有小于8kb的图片都转为base64形式(为什么呢?因为一个很小的图片,不值当的去发送一个请求,减少请求次                               数。)
                            //(其实应该说超过8kb的才使用 url-loader 来映射到文件,否则转为dataurl形式)
                        }
                    }
                ],
                //保证输出的图片名称与之前命名的图片名称保持一致(目前只是支持这样的写法,webpack3 没有响应的options进行配置)另外一种写法
                 use:'url-loader?limit=8192&name=[path][name].[ext]'
            }
        ]
    },
    devServer: {
        // contentBase: './dist',
        // 在 localhost:8080(默认) 下建立服务,将 dist 目录下的文件,作为可访问文件  contentBase:告诉服务器从哪里提供内容
        contentBase: path.join(__dirname, "dist"),
        compress: true,
        // 当它被设置为true的时候对所有的服务器资源采用gzip压缩
        // 对JS,CSS资源的压缩率很高,可以极大得提高文件传输的速率,从而提升web性能
        port: 9000, // 如果想要改端口,可以通过 port更改
        hot: true, // 启用 webpack 的模块热替换特性()
        inline: true, // 实现实时重载(实现自动刷新功能)默认情况下是 true。
        host: "localhost" // 如果你希望服务器外部可访问,指定使用一个 host。默认是 localhost(也就是你可以不写这个host这个配置属性)。
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: '首页', // 用于生成的HTML文档的标题
            filename: 'index.html', //写入HTML的文件。默认为index.html。也可以指定一个子目录(例如:)assets/admin.html
            template: 'index.html' // Webpack需要模板的路径
        }),
        new webpack.HotModuleReplacementPlugin() // 需要结合 启用热替换模块(Hot Module Replacement),也被称为 HMR
    ]
}

使用npm start命令就可以实现浏览器自动更新。

问题来了,HtmlWebpackPlugin中的 title并没有显示出来,原因是需要在定义的template模板中使用ejs语法,

<title><%= htmlWebpackPlugin.options.title%></title>

再次使用npm start命令就可以啦。

防止文件缓存(生成带有20位的hash值的唯一文件)

// webpack.config.js

output: {
        path: path.resolve(__dirname, 'dist/js'), // 指定出口文件的路径目录
        // filename: 'bulid.js' // 制定出口文件的名称
        filename: '[name].[hash].js' // 将入口文件重命名为带有20位的hash值的唯一文件
    }

抽取CSS为单独文件

安装插件从 build.js文件中提取文本(CSS)到单独的文件

cnpm install --save-dev extract-text-webpack-plugin

打包出错 Tapable.plugin is deprecated. Use new API on .hooks instead
原因 extract-text-webpack-plugin目前版本不支持webpack4。
解决方案 使用extract-text-webpack-plugin的最新的beta版
cnpm install -D extract-text-webpack-plugin@next

在webpack.config.js中配置

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


// 从 bundle 中提取文本(CSS)到单独的文件      
new ExtractTextPlugin({
    //  提取css,并重名为带有 20位的hash值的唯一文件
    filename: '[name].[hash].css',
    // 将所有的独立文件模块(这里指的是css文件)合并成一个文件
    allChunks: true
})

开发环境和生产环境的分离

开发环境与生产环境分离的原因如下:

  • 在开发环境中,我们使用热更新插件帮助我们实现浏览器的自动更新功能,我们的代码没有进行压缩,如果压缩了不方便我们调试代码等等,所以以上这些代码不应出现在生产环境中。
  • 生产环境中,我们把项目部署到服务器时,我们会对代码进行各种各样的优化,比如压缩代码等等,这时候我们不应该把这些代码放到开发环境中,不利于代码开发和调试。

总结:针对以上这些说明,我们很有必要把区分开发环境与生产环境分离。

开发环境的配置和生产换环境配置的区别。

  • 开发环境有的配置,生产环境不一定有,比如说热更新时使用到的HotModuleReplacementPlugin
  • 生产环境有的配置,开发环境不一定有,比如说用来压缩js用的UglifyJsPlugin

如何去做?

  1. 因为webpack 默认找的是 webpack.config.js配置文件,所以要把开发环境的webpack.config.js配置文件改为webpack.dev.config.js代表开发环境的配置文件。

  2. 新建一个webpack.prod.config.js,再把开发环境中的webpack.config.js复制进去(没用的配置文件该删除的删除

  3. 修改package.json文件(在scripts 标签中添加"dev""prod" 属性配置)

     "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1",
            "dev": "webpack --config webpack.dev.config.js",
            "prod": "webpack --config webpack.prod.config.js"
        },
    
  4. 怎样执行命令

    • 执行开发环境的中配置 cnpm run dev
    • 执行生产环境的中配置 cnpm run prod

在生产环境中配置代码压缩功能

安装插件

cnpm install  --save-dev  terser-webpack-plugin

配置webpack.prod.config.js 文件

  devtool: "source-map",
    // js代码 压缩
    optimization: {
        minimizer: [
            new TerserPlugin({
                cache: true, // 开启缓存
                parallel: true, // 支持多进程
                sourceMap: true,
            }),
        ]
    },

执行cnpm run prod 命令

webpack集成Vuejs开发

引入vue.js

项目中,我们会使用Vuejs进行开发,而且会以特殊的文件来组织vue的组件。所以,下面我们来学习一下如何在我们的webpack环境中集成Vuejs

现在,我们希望在项目中使用Vuejs,那么必然需要对其有依赖,所以需要先进行安装注:因为我们后续是在实际项目中也会使用vue的,所以并不是开发时依赖

npm install  vue --save

那么,接下来就可以按照我们之前学习的方式来使用Vue

main.js

import  Vue from 'vue'

new Vue({
    el:"#app",
    data:{
        name:"三月三"
    }
})

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack集成Vuejs</title>
</head>
<body>
<div id="app">
{{name}}
</div>

<script src="./dist/bulid.js"></script>
</body>
</html>

打包项目 – 错误信息

修改完成后,重新打包,运行程序:

打包过程没有任何错误(因为只是多打包了一个vue的js文件而已);但是运行程序,没有出现想要的效果,或浏览器中有报错

这个错误说的是我们使用的是runtime-only版本的Vue,所以我们修改webpack的配置,添加如下内容即可

resolve: {
    alias: {
        'vue$': 'vue/dist/vue.esm.js'
    }
},

el和template区别(一)

正常运行之后,我们来考虑另外一个问题:

**思考:**如果我们希望将data中的数据显示在界面中,就必须是修改index.html如果我们后面自定义了组件,也必须修改index.html来使用组件但是html模板在之后的开发中,我并不希望手动的来频繁修改,是否可以做到呢?

定义template属性:在前面的Vue实例中,我们定义了el属性,用于和index.html中的#app进行绑定,让Vue实例之后可以管理它其中的内容这里,我们可以将div元素中的{{message}}内容删掉,只保留一个基本的id为div的元素,但是如果我依然希望在其中显示{{message}}的内容,应该怎么处理呢?

我们可以再定义一个template属性,代码如下:

new Vue({
    el:"#app",
    template:`<div id="app">{{name}}</div>`,
    data:{
        name:"三月三"
    }
})

重新打包,运行程序,显示一样的结果和HTML代码结构。

那么,eltemplate模板的关系是什么呢?

  • el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。
  • 而如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。

这样做有什么好处呢?

这样做之后我们就不需要在以后的开发中再次操作index.html,只需要在template中写入对应的标签即可

但是,书写template模块非常麻烦怎么办呢?

没有关系,稍后我们会将template模板中的内容进行抽离。会分成三部分书写:template、script、style,结构变得非常清晰。

Vue组件化开发引入

Vue开发过程中,我们都会采用组件化开发的思想。那么,在当前项目中,如果我也想采用组件化的形式进行开发,应该怎么做呢?

查看下面的代码:当然,我们也可以将下面的代码抽取到一个js文件中,并且导出。

main.js

import  Vue from 'vue'

const  Cpn = {
    template:`<h2>{{name}}</h2>`,
    data(){
        return{
            name: "我是Cpn组件"
        }
    }

}

new Vue({
    el:"#app",
    template:`
        <div>我的名字:{{name}}
            <Cpn></Cpn>
        </div>`,
    data:{
        name:"三月三"
    },
    components:{
        Cpn
    }
})

但是一个组件以一个js对象的形式进行组织和使用的时候是非常不方便的

  1. 一方面编写template模块非常的麻烦
  2. 另外一方面如果有样式的话,我们写在哪里比较合适呢?

现在,我们以一种全新的方式来组织一个vue的组件。但是,这个时候这个文件可以被正确的加载吗?

必然不可以,这种特殊的文件以及特殊的格式,必须有人帮助我们处理。

谁来处理呢?

vue-loader以及vue-template-compiler。

安装vue-loadervue-template-compiler

cnpm install vue-loader vue-template-compiler --save-dev

创建.vue文件

<template>
    <h2 class="title">{{name}} </h2>
</template>

<script>
    export default {
        name: "Cpn",
        data(){
            return{
                name: "我们.vue文件"
            };
        }
    }
</script>

<style scoped>
.title{
    color: pink;
}
</style>

修改webpack.config.js的配置文件:

{
    //处理.vue文件
    test: /\.vue$/,
    use:['vue-loader']
},

注意: 打包报错?why

“vue-loader”: “^15.9.2”

  • 指定版本:比如"vue-loader": “15.9.2”,表示安装15.9.2的版本

  • 波浪号~+指定版本:比如 “vue-loader”: “~15.9.2”,表示安装15.9.x的最新版本(不低于15.9.0),但是不安装15.10.x,也就是说安装时不改变大版本号和次要版本号

  • ^+指定版本:比如 “antd”: “^3.1.4”,,表示安装3.1.4及以上的版本,但是不安装4.0.0,也就是说安装时不改变大版本号。

说vue-loader在被使用的时候缺少一个插件( corresponding plugin),vue-loader从14版本开始需要配置插件,如果不想配置插件,可以使用低于14的版本

解决:引入插件

 const VueLoaderPlugin = require('vue-loader/lib/plugin')
 
 plugins: [
    // 请确保引入这个插件来施展魔法
    new VueLoaderPlugin()
  ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李熠漾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值