4

demo地址:https://github.com/yonglijia/...

首先来介绍下webpack是干嘛的。

webpack简单的来讲,是一个前端模块化管理和打包工具,可以将你的文件所依赖的js,css,node包等全部打包成一个bundle文件,并且能够处理各种模块之间依赖问题。在webpack的世界里,一切皆模块!但它又不仅仅是一个打包工具。它不仅能够轻松处理你项目中的依赖关系和加载顺序,还能让这个流程更加智能化,自动化。

webpack的使用,最复杂的一部分是莫过于它的配置项。webpack通过你的配置项,放置所有与打包相关的信息。一个基本的配置包括

module.exports = {
        entry: '',
        output: {},
        module: {
            rules: []
        },
        plugins: [],
};

我们可以这样理解这几个配置:

你如果要打包一个文件,那首先要指定文件的地址,也就是entry;打包之后放在那里呢,也就是output;打包过程中文件要经过怎么样的处理,也就是rules中的loader;如何能够使webpack打包更快,体积更小呢,也就是plugins。这些配置相辅相成,紧密结合

这些配置命名需要写入webpack.config.js中。在项目中,执行webpack,就会自动引用这个文件。

下面我们详细介绍下这些配置。建立一个简单的项目,包含

1.jpg

testWebpck.js

module.exports = function() {
    var testDiv = document.createElement('div');
    testDiv.textContent = "hello world";
    return testDiv;
};

index.js

var testWebpack = require('./testWebpack.js');
document.querySelector("#root").appendChild(testWebpack());

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>learn webpack</title>
</head>
<body>
<div id="root">

</div>
<script src="build/bundle.js"></script>
</body>
</html>

webpack.config.js

module.exports = {
    entry: __dirname+"/app/index.js",//打包的js
    output: {
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "bundle.js",//打包后bundle文件的文件名
    },
    module: {
        rules: []
    },
    plugins: [],
};

我们在项目根目录中,执行webpack

2.jpg

浏览器打开index.html,可以看到hello world!。修改testWebpck, 修改为“hello webpack!”,重新执行webpack,再次打开index.html,可以看到浏览器中变为hello webpack!

开发环境

我们可以使用webpack-dev-server,来构建一个本地服务器,通过这个服务器,监听你的代码,实时更新你修改的内容。

首先

npm install webpack-dev-server

然后在package.json中添加脚本

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

npm的start命令是一个特殊的脚本,直接使用npm start就可以执行其对于的命令;而如果不是start,想要在命令行中运行时,需要这样用npm run {script name}npm run build

执行npm start,在浏览器中输入http://127.0.0.1:8080,可以看到我们所看到的一模一样的页面。

Webpac-dev-server需要添加devServer配置

devServer{
  hot:true,//
  inline:true,//
  port:8080,//默认8080
  proxy:{//接口代理
    '/xxx/**': {//接口匹配的地址
                target:"代理地址",
                secure: false
            },
  }
}

目前为止,我们已经使用wepack成功完成了一个文件的打包,并且能在开发环境中使用。但是未使用任何的loader,plugin,因为这里面涉及到的文件还太少,种类也不多。下面来一步步丰富这个小项目,引入所需要的配置。

我们在项目中引入css

index.css

#root{
    border:1px solid red;
}

在index.js中引入require('./index.css');

执行npm start

7.jpg
这时已经报错,提示我们要引入相应的loader来处理css。是时候展示真正的技术了——— loader。

loader

loader是什么呢?正如我们上面所讲的是,它用来如何处理我们的打包文件的;现在如果不引入loader,那就无法处理css文件。loader不仅仅是处理css,还可以用来css的预处理、js的中使用ES6等高级语法处理成浏览器能兼容的格式。文件、图片、json等处理,都需要用到loader;经过loader的处理,这些文件能够很好的打入打包后的bundle中。

一个loader所需要的配置包括四个方面

test、loader、includer/exclude、query

首先来看个示范

{
    test: /\.js$|\.jsx$/,
    loader: 'babel-loader',
    exclude: /(node_modules|bower_components)/,
    query: {
            presets: ['es2015', 'react'],
            plugins: ['transform-class-properties', 'lodash']
    },
},

test是一个正则表达式,它用来匹配适用于loader文件的扩展名

loader中是loader的名称,1.x版本不需要加上“-loader”,往后的版本需要加上“-loader”.

这两个都是必须添加的配置,下面两个是可选的。

include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)

query:为loaders提供额外的设置

CSS - loader

那我们来根据上面的配置来设置loader,来处理css。

webpack提供两个工具处理样式表,css-loader 和 style-loader;安装这两个npm包,修改我们的配置

module.exports = {
    entry: __dirname+"/app/index.js",//打包的js
    output: {
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "bundle.js",//打包后bundle文件的文件名
    },
    module: {
        rules: [ {test: /\.css$/, loaders: ["style-loader", "css-loader"]},]//引入多个loader,loader要写成loaders,属性为一个数组,存放各个loader
    },
    plugins: [],
};

执行npm start,在浏览器中我们发现,helllo world中已经添加上了红色的边框了。

css-loader使你能够使用类似@import和url(...)的方法实现require的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的js文件中

有一点要注意的是:loader是有顺序的。webpack肯定是先将所有css模块依赖解析完得到计算结果再创建style标签。因此应该把style-loader放在css-loader的前面(webpack loader的执行顺序是从右到左),顺序不对,会报错。

这样我们基本上使用了完成了一个loader的基本配置。

我们来配置下添加其他loader的配置项:

  • 处理图片

需要同时安装url-loader和file-loader

{test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},

url-loader与file-loader的工作方式相似,如果文件的体积比byte limit小,就能返回Data Url。如果图片比limit小,将直接以base64的形式内联在代码中。如果图片比limit(以bytes为单位)大,那么webpack就会使用file-loader去处理文件,并且所有的查询参数都会传递给file-loader。

我们想把js和图片打包成不同的文件夹,需要把output配置项修改一下

output: {
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "js/bundle.js",//打包后bundle文件的文件名
    },

在项目app文件夹中分别添加两个图片,一个大于1024,一个小于1024,circle_loaction.png,data_update.gif

添加上面的配置,执行weibpack,发现一个有一个文件在build/images中生成。

4.png

这里面有一个路径的点需要注意:

如果在output中添加publicPath,比如说/xxx/

module.exports = {
    entry: __dirname+"/app/index.js",//打包的js
    output: {
          publicPath: '/xxx/',
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "bundle.js",//打包后bundle文件的文件名
    },
    module: {
        rules: [
          {test: /\.css$/, loaders: ["style-loader", "css-loader"]},
          {test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
        ]
    },
    plugins: [],
};

那我们访问http://127.0.0.1:8080,发现bundle找不到。这是因为加上publicPath之后,访问内存中的文件bundle都需要加上/xxx/(但实际上引用的是内存中的文件,既不是/build/js/也不是/xxx/)。所以要修改html中的引用地址方能使用。

<script src="xxx/js/bundle.js"></script>

publicPath表示资源的发布地址,加上该属性后,打包文件中所有通过相对路径引用的资源都会被配置的路径所替换。

如果此时修改index.css

#root{
    border:1px solid red;
    height: 350px;
    background-image:url("./data_update.gif");
}

./data_update.gif 就会自动替换成xxx/images/data_update-37a1914078.gif。

path和publicPath的区别

path:/build/js

publicPaht:/online/

  • 线下环境

path是打包后文件存放的路径,不能用于html中的js引用

publicPath表示的是资源发布的路径。自动指向path编译目录(/online/ => /build/js/),html中引用js文件时,必须引用此虚拟路径。

  • 线上环境

webpack进行编译(当然是编译到/build/js/),我们需要把目录(/build/js/)下的文件,全部复制到/online/目录下(注意:不是去修改index.html中引用bundle.js的路径)

  • 处理jsx文件

webpack不能直接处理jsx,需要借助于babel.
babel堪称神器,被誉为下一代 JavaScript 语法的编译器。用它,你可以不用等浏览器的支持,就可以使用最新的标准的语法。使用它可以解析jsx的语法。对于babel,不做过多介绍。

首先安装

npm install --save-dev babel-cli babel-preset-env babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom 

创建 .babelrc

{
  "presets": ["react","es2015"]
}

添加loader

{
     test: /\.js$|\.jsx$/,
     exclude: /(node_modules|bower_components)/,
     loader: 'babel-loader',
}

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>learn webpack</title>
</head>
<body>
    <div id="root">

    </div>
    <div id="react">

    </div>
    <script src="build/js/index.js"></script>
    <script src="build/js/testReact.js"></script>
</body>
</html>

添加testWebpack.jsx,

import React from 'react';
import ReactDOM from 'react-dom';

class TestReact extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        return <div>this is a react div
            <img src={require('./data_update.gif')}/>
        </div>
    }
}
ReactDOM.render(<TestReact/>,document.getElementById('react'))

修改配置

module.exports = {
    entry: {
        index :__dirname+"/app/index.js",
        testReact:__dirname+"/app/testReact.jsx",
    },
    output: {
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "js/[name].js"//打包后输出文件的文件名
    },
    module: {
        rules: [
            {test: /\.css$/, loaders: ["style-loader", "css-loader"]},
            {test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
            {
                test: /\.js$|\.jsx$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
            }
        ]
    },
    plugins: [],
};

这里我们设置了两个入口文件,并且打包成不同的js,这个js的名字和他们本身的js的名字相同,通过filename: "js/[name].js"指定。

7.jpg

这样设置完我们就完成了使用babel-loader处理jsx文件。

稍微展开一点entry:

entry 有三种类型字符串,数组和对象

  1. entry:"xxx/index.js",

  2. entry:["xxx/index.js","xxx/index2.js"],

  3. entry:{
    index:"xxx/index.js",
    index2:"xxx/index2.js"
    },

plugin

下面我们介绍下,如何添加插件,使我们的打包工程更快,更智能。

先介绍下一些常用的插件。

  • uglifyjs-webpack-plugin :JS压缩

var webpack = require('webpack');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
    entry: {
        index :__dirname+"/app/index.js",
        testReact:__dirname+"/app/testReact.jsx",
    },
    output: {
        path: __dirname + "/build",//打包后的文件存放的地方
        filename: "js/[name].js"//打包后输出文件的文件名
    },
    module: {
        rules: [
            {test: /\.css$/, loaders: ["style-loader", "css-loader"]},
            {test: /\.(?:jpg|gif|png)$/, loader: 'url-loader?limit=10240&name=images/[name]-[hash:10].[ext]'},
            {
                test: /\.js$|\.jsx$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel-loader',
            }
        ]
    },
    plugins: [
      new UglifyJSPlugin(),
    ],
};

我们这时候打开build/js中的js文件,发现里面都被压缩混淆了。

  • DefinePlugin:定义全局变量

  • Hot Module Replacement:

    在webpack中实现HMR很简单,只需要两个配置

    1. 添加new webpack.HotModuleReplacementPlugin()

    2. 修改脚本 "start": "webpack-dev-server --config webpack.config.js",

插件分为内置和外置的,不过用法都是大同小异的。不同的插件,配置参数也不一样。

其他配置

  • devtool:这个是用来配置soucemap的类型,在开发的时候调试特别有用。里面的配置特别多,后面会单独介绍这个地方。

  • resolve:配置短路径引用

     resolve: {
            alias: {
                module: path.resolve(APP_PATH, 'module'),
                component: path.resolve(APP_PATH, "component"),
                service: path.resolve(APP_PATH, "service"),
                page: path.resolve(APP_PATH, "page"),
                node_modules: path.resolve(ROOT_PATH, 'node_modules')
            },
            extensions: ['.js', '.jsx', '.json', '.scss']
        },

    使用这个选项的话,你可以直接使用比如require('page/index'),其实就是path.resolve(APP_PATH, "page")+'/index',extensions是用来匹配所要引用的文件类型,匹配以index开头的 ['.js', '.jsx', '.json', '.scss']文件,如果没有,就会报错。

这篇文章权当个入门文档,里面还有很多需要一一深入挖掘的,比如devtool到底选择哪个最好,怎么使项目的打包更快,怎么使用dll等等,后续会一点一点的写。


儿独
729 声望25 粉丝