平常工做webpack必备姿式(一)

为何用webpack这玩意儿?

你们有没有看过jquery框架的源码?知道它源码有多少行吗?css

1.x版本的都在10000行以上。你们试想若是在开发时候这1万多行代码都在一个文件,那文件这么长,开发测试的时候会有多麻烦。因而乎有人就开始想了,开发的时候把代码按照不一样的功能分红不一样的文件,方便于开发调试,到发布的时候在把代码合并到一块儿就OK了,这个把代码合并到一块儿的玩意儿就是咱们今天须要探讨的打包工具,也是它的最主要的功能之一:代码合并。html

在咱们前端开发中,你们必定还遇到过如下一些常见问题,诸如:前端

前端样式采用less或者scss开发,最新的es6语法,但是浏览器并不能识别(备注:能够引入转换脚本解决)node

项目引入的css和js等文件太多,文件体积太大,势必会引发浏览器屡次请求服务器加载资源引发速度慢,能不能减小文件数量和文件体积呢?jquery

这就是今天的主角webpack要作的事情,看下去你就会知道如何使用!webpack

webpack安装和入门案例

先来看看安装:es6

//初始化安装目录
npm init -y

安装webpack和webpack-cli(4.x以上版本须要安装webpack-cli)
npm install --save-dev webpack
npm install --save-dev webpack-cli

安装完咱们来体验一下入门案例:
企业微信截图_20200314102945.pngweb

第一个案例整体目录如图所示:正则表达式

第一步:创建src文件,在src文件下新建文件取名为index.jsnpm

//index.js内容以下
console.log("hello webpack");

第二步:使用webpack打包上面的文件

npx webpack

自此变自动生成了如图所示的dist目录和打包后的main.js文件

第三步:验证打包后的main.js文件是否正确

方法一:用html文件测试,咱们的例子是会在控制台输出"hello webpack"字符串

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>webpack</title>        
    </head>
    <body>
    </body>
    <script src="./main.js"></script>
</html>

方式二:由于咱们这里只用到了js代码,能够进入到打包后的文件夹,用node来运行代码

node main.js

编译后的文件默认是生产环境,代码通过压缩因此直接看不懂,若是你但愿能看懂编译后的js文件,或者你但愿修改一下编译后的文件名称等,能够经过给webpack提供配置文件的方式来解决:

webpack配置文件

第一步:在项目目录下创建文件webpack.config.js

文件内容以下

let path =  require('path'); //webpack是node写出来的,path是node的语法
 
module.exports = {
    mode:'development', //编译环境改为是development(开发模式)
    entry:'./src/index.js',    //须要编译的源文件目录
    output:{                //编译后的目录
        filename:'configTest.js',    //编译后的文件名称
        path:path.resolve(\_\_dirname,'build')    //编译后的路径,必须是绝对路径
    }
}

第二步:编译打包文件和测试生成的文件方式和上一步相同,不过注意生成的文件名称变了

npx webpack  //和以前的编译方式同样

附上一张最终效果图:
企业微信截图_20200314110206.png

配置文件名称能不能改?我公司的项目在打包编译的时候用的是npm run build是怎么回事?

好比我如今将webpack配置文件名改为了webpack.configOther.js,那么我在运行的时候能够经过指定配置文件名称的方式运行,以下:

npx webpack --config webpack.configOther.js

可是上述运行代码的方式明显太长,看着很不舒服,只需一步就能够搞定:

//打开package.json文件,添加以下key-value值
"scripts": {
    "build": "webpack --config webpack.configOther.js"
  },

接下来咱们就能够经过npm run build的方式来编译咱们的代码了。写在package.json文件scripts里的内容能够经过npm run 脚本名的方式来调用。

webpack开发环境搭建

上面咱们测试本身写的webpack用法对不对,须要想办法运行编译后的文件才行,有没有以为很麻烦?咱们编译后的文件还须要手动在新建的html文件里面引入,每更改一次文件要想看更新后的效果都还须要在次刷新页面。。。问题多的我已经写不下去了,咱们来看看解决之道吧。

webpack插件的概念:一些第三方项目工具包,webpack中引入这玩意可以对整个工程全部代码进行处理,丰富webpack的功能。

html-webpack-plugin插件:引入这位老兄,咱们的项目能够自动在编译后的路径中生成html文件,而且自动引用编译后的js文件。

devServer:这个工具能够帮助咱们实现热开发,它能在本地启动http服务,咱们经过浏览器访问项目。咱们的项目代码会被加载到内存中。咱们修改了本地的代码,不须要重启服务,项目会自动更新为最新状态。

介绍了这么多咱们就来实际操做一遍:
第一步:安装依赖包

//安装html-webpack-plugin
npm install --save-dev html-webpack-plugin
//安装webpack-dev-server
npm install webpack-dev-server --save-dev

第二步:修改webpack.config.js文件

let path =  require('path'); //webpack是node写出来的,path是node的语法
let HtmlWebpackPlugin = require('html-webpack-plugin'); //HTML编译插件

module.exports = {
    //开发环境
    devServer: {
        contentBase: './build',        //咱们把编译后的目录build指定为开发环境
        compress: true,    //是否展现进度条
        port: 9000    //开发环境启动端口
    },

    mode:'development', //编译环境改为是development(开发模式)
    entry:'./src/index.js',    //须要编译的源文件目录
    output:{                //编译后的目录
        filename:'bundle.[hash].js',    //编译后的文件名称
        path:path.resolve(__dirname,'build')    //编译后的路径,必须是绝对路径
    },

    plugins:[
        new HtmlWebpackPlugin({
            template:'./src/index.html', //须要编译的html源文件
            filename:'indexTest.html',         //编译后的文件名
            minify:{
                collapseWhitespace:true     //编译后的html文件去掉空格
            }
        })
    ]
}

上述devServer是开发环境热加载功能的部分,HtmlWebpackPlugin是自动打包html文件的插件用法,注意template是须要有一个源文件的,filename指定编译后的目标文件名称(我这里随意指定的,通常取名为index.html)

//./src/index.html源文件,我这里没有指定js文件名称
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>webpack</title>        
    </head>
    <body>        
    </body>    
</html>

第三步:查看html-webpack-plugin编译后的效果

//编译查看效果
npm run build

企业微信截图_20200314163024.png

我在上面的配置中指定了,生成的js文件名称为hash方式,这样能够防止文件没有刷新和缓存引发的问题,咱们在源html文件中并未指定生成的js文件会自动引入。

第四步:配置开发环境
就像以前配置npm run build同样,咱们配置一下开发环境运行方式为npm run dev

"scripts": {
    "build": "webpack --config webpack.config.js",
    "dev": "webpack-dev-server"
  },

接下来咱们就能够经过npm run dev来开发咱们的项目啦。

为了让浏览器看到效果,咱们在经过js文件想html中输入一段文字

//修改咱们的index.js文件
document.body.innerHTML= 'devServer and HtmlWebpackPlugin';

在浏览器中输入项目路径便可访问:

http://localhost:9000/indexTest.html
//若是生成的文件名为index.html.可省略文件名,直接用以下方式调用
http://localhost:9000

接下来咱们能够随意修改index.js文件的内容,保存以后页面会当即刷新最新数据

样式处理

开发中避免不了须要给页面添加css样式,但是webpack是node开发的,不认识css文件,咱们如何让它认识呢?

有一个常接触到朋友叫loader,好比如今有一个css文件咱们须要解析怎么办?这个时候咱们只要在webpack中添加上style-loader css-loader就可让webpack识别css文件了。再好比咱们css用的是less语法,咱们只要在webpack中添加less-loader就又能够用了。看到了吗?loader它就是用于专门处理一类文件的,功能比较单一,一种loader一般只处理一种文件。咱们看下示例:

第一步:安装依赖包

//安装解析css的依赖包
npm install --save-dev style-loader css-loader
//安装解析less的依赖包(不用less可不安装)
npm install less less-loader --save-dev

第二步:准备好样式文件

//001.css文件
@import './002.css';
body{
    background-color: green;
}

//002.css文件
body{
    color:red;
}

//003.less文件
body{
    font-size:30px;
}

第三步:要在项目中引入这些样式才能看到效果(主入口文件中引入)

//我这里的主入口文件一直都是index.js(webpack配置文件中的entry:'./src/index.js'选项)
require('./001.css')
require('./003.less')
document.body.innerHTML= 'loader';

第四步:在配置文件中告诉webpack如何解析,添加新的属性(可参考结束语的整个配置文件)

//模块
module:{
    //规则:loader特色,但愿单一
    //loader的用法,字符串只适用于一个loader,多个loader须要用[]
    //loader的顺序,默认是从右向左执行
    //loader还能够写出对象的方式(好处是能够添加更多的参数)
    rules:[
        {
            test:/\.css$/,
            use:[
                //css-loader用于机械@import这种语法
                //style-loader把css插入到页面中
                {
                    loader:'style-loader'
                },
                'css-loader'
            ]
        },

        {
            test:/\.less$/,
            use:[
                'style-loader',
                'css-loader',
                'less-loader' //less转换成css文件
            ]
        }
    ]
}

loader须要放到module下的rules中,一个项目中有各类文件须要解析,所以rules是一个数组,能够配置不一样的解析规则。每个loader配置包含test属性,是一个正则表达式用于匹配文件格式;use则用于指定具体的解析loader。

咱们来查看最终的效果图:
企业微信截图_20200314182910.png

备注:抽离css可使用mini-css-extract-plugin

转换es6语法

如今的前端开发避免不了要用到ES6,不过您不了解ES6和babel请跳过本部分知识。

第一步:安装相应的loader

npm install -D babel-loader @babel/core @babel/preset-env webpack

第二步:在webpack配置文件中module下rules中添加文件过滤规则

{
    test:/\.js$/,
    use:{
        loader:'babel-loader',
        options:{
            presets: ['@babel/preset-env'] //指定将ES6转换为ES5
        }
    }
}

搞定完这两步就能够在项目中使用ES6语法了,想测试的话能够将mode修改成development,而后在编译文件中查看是否有转换成ES5语法。

eslint校验

eslint是项目代码校验工具,可以在项目运行启动以前检查项目语法错误,若是对eslint不是很了解,请先点击连接看下相关文档

第一步:安装eslint和loader

npm install eslint eslint-loader --save-dev

第二步:在webpack配置文件中module下rules中添加文件过滤规则

{
    test: /\.js$/,
    exclude: /node_modules/, //去掉不须要校验的模块
    loader: 'eslint-loader', 
    options: {
      failOnError: true,
    },
},

第三步:在项目目录下添加.eslintrc.json文件(注意文件名称前有英文点号)

//.eslintrc.json文件内容,能够根据实际须要自行配置
{
    "parserOptions": {
        "ecmaVersion": 5,
        "sourceType": "script",
        "ecmaFeatures": {}
    },
    "rules": {
        "constructor-super": 2,
        "for-direction": 2,
        "getter-return": 2,
        "no-async-promise-executor": 2,
        "no-case-declarations": 2,
        "no-class-assign": 2,
        "no-compare-neg-zero": 2,
        "no-cond-assign": 2,
        "no-const-assign": 2,
        "no-constant-condition": 2,
        "no-control-regex": 2,
        "no-debugger": 2,
        "no-delete-var": 2,
        "no-dupe-args": 2,
        "no-dupe-class-members": 2,
        "no-dupe-keys": 2,
        "no-duplicate-case": 2,
        "no-empty": 2,
        "no-empty-character-class": 2,
        "no-empty-pattern": 2,
        "no-ex-assign": 2,
        "no-extra-boolean-cast": 2,
        "no-extra-semi": 2,
        "no-fallthrough": 2,
        "no-func-assign": 2,
        "no-global-assign": 2,
        "no-inner-declarations": 2,
        "no-invalid-regexp": 2,
        "no-irregular-whitespace": 2,
        "no-misleading-character-class": 2,
        "no-mixed-spaces-and-tabs": 2,
        "no-new-symbol": 2,
        "no-obj-calls": 2,
        "no-octal": 2,
        "no-prototype-builtins": 2,
        "no-redeclare": 2,
        "no-regex-spaces": 2,
        "no-self-assign": 2,
        "no-shadow-restricted-names": 2,
        "no-sparse-arrays": 2,
        "no-this-before-super": 2,
        "no-undef": 2,
        "no-unexpected-multiline": 2,
        "no-unreachable": 2,
        "no-unsafe-finally": 2,
        "no-unsafe-negation": 2,
        "no-unused-labels": 2,
        "no-unused-vars": 2,
        "no-useless-catch": 2,
        "no-useless-escape": 2,
        "no-with": 2,
        "require-atomic-updates": 2,
        "require-yield": 2,
        "use-isnan": 2,
        "valid-typeof": 2
    },
    "env": {
        "browser": true,
        "node": true
    }
}

第四步:编写文件,开启项目或者编译项目检查eslint是否正确配置

//在index.js文件中添加以下代码启动项目,能够看到如图所示的错误
var foo = bar;

企业微信截图_20200314234851.png

即:
'foo' is assigned a value but never used no-unused-vars
'bar' is not defined no-undef

引入第三方插件和全局变量

有时候可能须要在项目中引入jquery等第三方库,该怎么作呢?

首先没必要多说确定是引入jquery

npm run jquery

而后能够直接在想用jquery的地方引入jquery便可

//node的方式
let $ =  require('jquery');
//ES6的方式
import $ from 'jquery'

不过上述引入jquery方式都只是在单个模块内部,也就是说每一个模块文件想用jquery都得从新引入一次jquery才行,不能经过window获取jquery对象。但若是你就但愿经过window.$来用怎么办呢?

方式一:经过expose-loader插件把$暴露到全局window对象上

//安装expose-loader
npm run expose-loader
//在文件中引入jquery(不可缺乏)
let $ =  require('jquery');
//在module下rules中添加文件过滤规则
{
    test:require.resolve('jquery'),
    use:'expose-loader?$'
},
//至此能够console.log(window.$)获取全局$对~~~~象

方式二:一次性在全部模块中注入$对象,这种方式并不会jquery暴露到window对象上,可是每一个模块在使用$的时候也不须要在引用jquery了

//在配置文件中引入webpack
let webpack = require('webpack');
//在配置文件中plugins添加以下插件
new webpack.ProvidePlugin({
            $:'jquery'~~~~
        })

方式三:直接在页面的<script>标签中引入js文件,不经过webpack打包的方式。

引入图片和路径处理

JS引入图片

第一步:安装依赖loader

//图片属于文件
npm install --save-dev file-loader

第二步:在配制文件中module下的rules中添加规则

{
    test: /\.(png|svg|jpg|gif)$/,
    use: 'file-loader'
},

第三步:在项目目录下添加图片

import imgLogo from './0.jpg';
let image = new Image();
image.src = imgLogo;
document.body.appendChild(image);

CSS中引入图片

第一步:在css中引入图片

body{
    background: url('./0.jpg');
}

第二步:在入口文件中引入css文件(index.js文件)

import('./001.css')

备注:html-withimg-loader能够实现html中直接使用img标签src加载图片,请自行学习~

文件打包路径

在开发过程当中,咱们应该将全部图片都放到img目录下,能够经过修改上面的loader配置来实现

{
    test: /\.(png|svg|jpg|gif)$/,
    use: {
        loader:'file-loader',
        options:{
            outputPath:'img/' //建立img目录
        }
    }
},

或许还会根据须要将全部文件放到么个域名下,咱们能够在编译的时候,修改目录输出,添加publicPath属性便可。

//编译后的目录
output:{                
        filename:'bundle.[hash].js',    //编译后的文件名称
        path:path.resolve(__dirname,'build'),    //编译后的路径,必须是绝对路径
        publicPath:'http://www.baidu.com/~~~~' //编译到么个域名下
    },

结束语

都目前为止整个项目配置文件内容以下,可根据须要进行选用:

let path =  require('path'); //webpack是node写出来的,path是node的语法
let HtmlWebpackPlugin = require('html-webpack-plugin'); //HTML编译插件
let webpack = require('webpack');

module.exports = {
    //开发环境
    devServer: {
        contentBase: './build',        //咱们把编译后的目录build指定为开发环境
        compress: true,    //是否展现进度条
        port: 9000    //开发环境启动端口
    },

    mode:'development', //编译环境改为是development(开发模式)
    entry:'./src/index.js',    //须要编译的源文件目录
    output:{                //编译后的目录
        filename:'bundle.[hash].js',    //编译后的文件名称
        path:path.resolve(__dirname,'build'),    //编译后的路径,必须是绝对路径
        //publicPath:'http://www.baidu.com'
    },

    plugins:[
        new HtmlWebpackPlugin({
            template:'./src/index.html', //须要编译的html源文件
            filename:'index.html',         //编译后的文件名            
        }),
        new webpack.ProvidePlugin({
            $:'jquery'
        })
    ],
    
    //模块
    module:{
        //规则:loader特色,但愿单一
        //loader的用法,字符串只适用于一个loader,多个loader须要用[]
        //loader的顺序,默认是从右向左执行
        //loader还能够写出对象的方式(好处是能够添加更多的参数)
        rules:[           
            // {
            //     test: /\.js$/,
            //     exclude: /node_modules/, //去掉不须要校验的模块
            //     loader: 'eslint-loader', 
            //     options: {
            //       failOnError: true,
            //     },
            // },

            {
                test:require.resolve('jquery'),
                use:'expose-loader?$'
            },

            {
                test:/\.css$/,
                use:[
                    //css-loader用于机械@import这种语法
                    //style-loader把css插入到页面中
                    {
                        loader:'style-loader'
                    },
                    'css-loader'
                ]
            },

            {
                test:/\.less$/,
                use:[
                    'style-loader',
                    'css-loader',
                    'less-loader' //less转换成css文件
                ]
            },

            {
                test:/\.js$/,
                use:{
                    loader:'babel-loader',
                    options:{
                        presets: ['@babel/preset-env'] //指定将ES6转换为ES5
                    }
                }
            },

            {
                 test: /\.(png|svg|jpg|gif)$/,
                use: {
                    loader:'file-loader',
                    options:{
                        outputPath:'img/'
                    }
                }
            },
            
        ]
    }
}
相关文章
相关标签/搜索