zoukankan      html  css  js  c++  java
  • 转:Webpack 指南(整理 草稿)

    基础

    安装

    首先要安装 Node.js, Node.js 自带了软件包管理器 npm。用 npm 全局安装 Webpack:

    npm install webpack -g

    通常我们会将 Webpack 安装到项目的依赖中,这样就可以使用项目本地版本的 Webpack。

    # 进入项目目录,初始化,创建 package.json。
    # 若存在 package.json 文件,则不运行。
    $ npm init
    # 确定已经有 package.json
    # 安装 webpack 依赖
    $ npm install webpack --save-dev

    如果需要使用 Webpack 开发工具,要单独安装:

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

    使用

    首先创建一个静态页面 index.html 和一个 JS 入口文件 entry.js

    <!-- index.html -->
    <html>
    <head>
      <meta charset="utf-8">
    </head>
    <body>
      <script src="bundle.js"></script>
    </body>
    </html>
    // entry.js
    document.write('It works.')

    然后编译 entry.js 并打包到 bundle.js

    $ webpack entry.js bundle.js

    用浏览器打开 index.html 将会看到

    It works.

    最终目录结构如下:

    .
    ├── entry.js
    ├── index.html
    ├── package.json
    ├── node_modules

    接下来添加一个模块 module.js 并修改入口 entry.js

    // module.js
    module.exports = 'It works from module.js.'
    // entry.js
    document.write('It works.')
    document.write(require('./module.js')) // 添加模块

    重新打包 webpack entry.js bundle.js 后刷新页面看到变化

    It works.It works from module.js.

    最终目录结构如下:

    .
    ├── bundle.js
    ├── entry.js
    ├── index.html
    ├── module.js
    ├── package.json
    ├── node_modules

    进阶

    使用 Loader

    Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。Loader 可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果。这样,我们就可以通过 require 来加载任何类型的模块或文件,比如 CoffeeScript、 JSX、 LESS 或图片。

    先来看看 loader 有哪些特性?

    • Loader 可以通过管道方式链式调用,每个 loader 可以把资源转换成任意格式并传递给下一个 loader ,但是最后一个 loader 必须返回 JavaScript。

    • Loader 可以同步或异步执行。

    • Loader 运行在 node.js 环境中,所以可以做任何可能的事情。

    • Loader 可以接受参数,以此来传递配置项给 loader。

    • Loader 可以通过文件扩展名(或正则表达式)绑定给不同类型的文件。

    • Loader 可以通过 npm 发布和安装。

    • 除了通过 package.json 的 main 指定,通常的模块也可以导出一个 loader 来使用。

    • Loader 可以访问配置。

    • 插件可以让 loader 拥有更多特性。

    • Loader 可以分发出附加的任意文件。

    Loader 本身也是运行在 node.js 环境中的 JavaScript 模块,它通常会返回一个函数。大多数情况下,我们通过 npm 来管理 loader,但是你也可以在项目中自己写 loader 模块。

    按照惯例,而非必须,loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 json-loader

    除了npm安装模块的时候以外,在任何场景下,loader名字都是可以简写的。例如:安装时必须用全名,即:npm install json-loader,而在引用 loader 的时候可以使用全名 json-loader,也可以使用短名 json。这个命名规则和搜索优先级顺序在 webpack 的 resolveLoader.moduleTemplates api 中定义。

    Default: ["*-webpack-loader", "*-web-loader", "*-loader", "*"]

    Loader 可以在 require() 引用模块的时候添加,也可以在 webpack 全局配置中进行绑定,还可以通过命令行的方式使用。

    loader是可以串联使用的,也就是说,一个文件可以先经过A-loader再经过B-loader最后再经过C-loader处理。而在经过所有的loader处理之前,webpack会先取到文件内容交给第一个loader。

    接上一节的例子,我们要在页面中引入一个 CSS 文件 style.css,首先将 style.css 也看成是一个模块,然后用 css-loader来读取处理(路径处理、import处理等),然后经过 style-loader 处理(包装成JS文件,运行的时候直接将样式插入DOM中)。

    /* style.css */
    body { background: yellow; }

    修改 entry.js

    // entry.js
    require("!style!css!./style.css") // 载入 style.css
    document.write('It works.')
    document.write(require('./module.js'))

    安装 loader:

    # css-loader:读取 css 文件
    # style-loader:将 css 文件插入页面
    $ npm install css-loader style-loader --save-dev

    重新编译打包,刷新页面,就可以看到黄色的页面背景了。

    如果每次 require CSS 文件的时候都要写 loader 前缀,是一件很繁琐的事情。我们可以根据模块类型(扩展名)来自动绑定需要的 loader。

    将 entry.js 中的 require("!style!css!./style.css") 修改为 require("./style.css") ,然后执行:

    $ webpack entry.js bundle.js --module-bind 'css=style!css'

    显然,这两种使用 loader 的方式,效果是一样的。最终的目录结构如下:

    .
    ├── bundle.js
    ├── entry.js
    ├── index.html
    ├── module.js
    ├── node_modules
    ├── package.json
    ├── style.css

    loader还可以接受参数,不同的参数可以让loader有不同的行为(前提是loader确实支持不同的行为),具体每个loader支持什么样的参数可以参考loader的文档。loader的使用有三种方法,分别是:

    • 在require中显式指定,如:

    • 在命令行中指定,如:$ webpack entry.js output.js --module-bind 'css=style!css'

    • 在配置项(webpack.config.js)中指定,如:

    第一种显式指定,即在 JS 文件中指定:

    require('style!css!./style.css');`

    第二种在命令行中指定参数的用法用得较少,可以这样写:

    $ webpack --module-bind jade --module-bind 'css=style!css'

    使用 --module-bind 指定loader,如果后缀和loader一样,直接写就好了,比如jade表示.jade文件用jade-loader处理,如果不一样,则需要显示指定,如 css=style!css 表示分别使用 css-loader 和 style-loader 处理 .css 文件。

    第三种在配置项中指定是最灵活的方式,它的指定方式是这样:

    module: {
        // loaders是一个数组,每个元素都用来指定loader
        loaders: [{
            test: /.jade$/,    //test值为正则表达式,当文件路径匹配时启用
            loader: 'jade',    //指定使用什么loader,可以用字符串,也可以用数组
            exclude: /regexp/, //可以使用exclude来排除一部分文件
    
            //可以使用query来指定参数,也可以在loader中用和require一样的用法指定参数,如`jade?p1=1`
            query: {
                p1:'1'
            }
        },
        {
            test: /.css$/,
            loader: 'style!css'    //loader可以和require用法一样串联
        },
        {
            test: /.css$/,
            loaders: ['style', 'css']    //也可以用数组指定loader
        }]
    }

    注意: 用数组指定串联loader时,配置文件中要写 loaders,而非 loader

    配置文件

    Webpack 在执行的时候,除了在命令行传入参数,还可以通过指定的配置文件来执行。默认情况下,会搜索当前目录的 webpack.config.js 文件,这个文件是一个 node.js 模块,返回一个 json 格式的配置信息对象,或者通过 --config 选项来指定配置文件。

    继续我们的案例,创建配置文件 webpack.config.js

    var webpack = require("webpack")
    module.exports = {
        entry: './entry.js',
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [
                { test: /.css$/, loader: 'style!css' }
            ]
        }
    }

    同时简化 entry.js 中的 style.css 加载方式:

    require('./style.css')

    最后运行 webpack,可以看到 webpack 通过配置文件执行的结果和上一节通过命令行 webpack entry.js bundle.js --module-bind 'css=style!css' 执行的结果是一样的。

    插件

    插件可以完成更多 loader 不能完成的功能。
    插件的使用一般是在 webpack 的配置信息 plugins 选项中指定。
    Webpack 本身内置了一些常用的插件,还可以通过 npm 安装第三方插件。
    接下来,我们利用一个最简单的 BannerPlugin 内置插件来实践插件的配置和运行,这个插件的作用是给输出的文件头部添加注释信息。

    修改 webpack.config.js,添加 plugins:

    var webpack = require('webpack')
    
    module.exports = {
      entry: './entry.js',
      output: {
        path: __dirname,
        filename: 'bundle.js'
      },
      module: {
        loaders: [
          {test: /.css$/, loader: 'style!css'}
        ]
      },
      plugins: [
        new webpack.BannerPlugin('This file is created by zhaoda')
      ]
    }

    然后运行 webpack,打开 bundle.js,可以看到文件头部出现了我们指定的注释信息:

    /*! This file is created by zhaoda */
    /******/ (function(modules) { // webpackBootstrap
    /******/  // The module cache
    /******/  var installedModules = {};
    // 后面代码省略

    参数详解

    entry

    entry参数定义了打包后的入口文件,可以是个字符串或数组或者是对象;如果是数组,数组中的所有文件会打包生成一个filename文件;如果是对象,可以将不同的文件构建成不同的文件:

    {
        entry: {
            page1: "./page1",
            //支持数组形式,将加载数组中的所有模块,但以最后一个模块作为输出
            page2: ["./entry1", "./entry2"],
            
            // 如果想保持目录结构,则直接按照目录结构命名
           'subapp1/page': './app/subapp1/page.js',
           'subapp2/page': './app/subapp2/page.js',
        },
        output: {
            path: "dist/js/page",
            publicPath: "/output/",
            filename: "[name].bundle.js"
        }
    }

    该段代码最终会在 ./dist/js/page 文件夹下生成如下结构:

    │  page1.bundle.js 
    │  page2.bundle.js
    │
    ├─subapp1
    │      page.bundle.js
    │      
    └─ssubapp2
           page.bundle.js

    保持目录结构命名的方式,在构架大型应用中非常有用。

    output

    output参数是个对象,定义了输出文件的位置及名字:

    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: 'http://localhost:3000/static/',
        filename: "js/[name].bundle.js"
    }
    • path: 打包文件存放的绝对路径

    • publicPath: 网站运行时的访问路径

    • filename:打包后的文件名

    当我们在entry中定义构建多个文件时,filename可以对应的更改为[name].js用于定义不同文件构建后的名字。
    如下 'http://localhost:3000/static/' 一般我们做调试时的路径,如果我们要在网页中引用 js 文件,html 文件中的路径写为:http://localhost:3000/static/js/<name>.bundle.js
    即 <publicPath>+<filename>

    module

    在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等静态文件都是模块,不同模块的加载是通过模块加载器(webpack-loader)来统一管理的。loaders之间是可以串联的,一个加载器的输出可以作为下一个加载器的输入,最终返回到JavaScript上:

    module: {
            //加载器配置
            loaders: [
                //.css 文件使用 style-loader 和 css-loader 来处理
                { test: /.css$/, loader: 'style-loader!css-loader' },
                //.js 文件使用 jsx-loader 来编译处理
                { test: /.js$/, loader: 'jsx-loader?harmony' },
                //.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
                { test: /.scss$/, loader: 'style!css!sass?sourceMap'},
                //图片文件使用 url-loader 来处理,小于8kb的直接转为base64
                { test: /.(png|jpg)$/, loader: 'url-loader?limit=8192'}
            ]
        }
    字段说明
    test 表示匹配的资源类型
    loader 或 loaders 表示用来加载这种类型的资源的loader
    定义loader的串联关系,多个loader之间用“!”连接起来

    此外,还可以添加用来定义png、jpg这样的图片资源在小于10k时自动处理为base64图片的加载器:

    test: /.(png|jpg)$/,loader: 'url-loader?limit=10000'}

    给css和less还有图片添加了loader之后,我们不仅可以像在node中那样 require() js文件了,我们还可以 require() css、less甚至图片文件:

     require('./bootstrap.css');
     require('./myapp.less');
     var img = document.createElement('img');
     img.src = require('./glyph.png');

    注意,require() 还支持在资源path前面指定loader,即 require(![loaders list]![source path])形式:

    require("!style!css!less!bootstrap/less/bootstrap.less");
    // “bootstrap.less”这个资源会先被"less-loader"处理,
    // 其结果又会被"css-loader"处理,接着是"style-loader"
    // 可类比pipe操作

    require() 时指定的loader会覆盖配置文件里对应的loader配置项。

    resolve

    webpack在构建包的时候会按目录的进行文件的查找,resolve 属性中的 extensions 数组中用于配置程序可以自行补全哪些文件后缀:

     resolve: {
            //查找module的话从这里开始查找
            root: '/pomy/github/flux-example/src', //绝对路径
            //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
            extensions: ['', '.js', '.json', '.scss'],
            //模块别名定义,方便后续直接引用别名,无须多写长长的地址
            alias: {
                AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
                ActionType : 'js/actions/ActionType.js',
                AppAction : 'js/actions/AppAction.js'
            }
        }

    然后我们想要加载一个js文件时,只要 require('common') 就可以加载 common.js 文件了。
    注意一下, extensions 第一个是空字符串! 对应不需要后缀的情况.

    plugin

    webpack提供了[丰富的组件]用来满足不同的需求,当然我们也可以自行实现一个组件来满足自己的需求:

    plugins: [
         //your plugins list
     ]

    在webpack中编写js文件时,可以通过require的方式引入其他的静态资源,可通过loader对文件自动解析并打包文件。通常会将js文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程中我们并不想将样式打在脚本中,最好可以独立生成css文件,以外链的形式加载。这时 extract-text-webpack-plugin 插件可以帮我们达到想要的效果。需要使用npm的方式加载插件,然后参见下面的配置,就可以将js中的css文件提取,并以指定的文件名来进行加载。

    npm install extract-text-webpack-plugin –-save-dev
    plugins: [
        new ExtractTextPlugin('styles.css')
    ]

    externals

    当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题:

     externals: {
         "jquery": "jQuery"
     }

    这样我们就可以放心的在项目中使用这些API了:var jQuery = require(“jquery”);

    context

    当我们在require一个模块的时候,如果在require中包含变量,像这样:

    require("./mods/" + name + ".js");

    那么在编译的时候我们是不能知道具体的模块的。但这个时候,webpack也会为我们做些分析工作:

    1.分析目录:’./mods’; 2.提取正则表达式:’/^.*.js$/’;

    于是这个时候为了更好地配合wenpack进行编译,我们可以给它指明路径,像在cake-webpack-config中所做的那样(我们在这里先忽略abcoption的作用):

     var currentBase = process.cwd();
     var context = abcOptions.options.context ? abcOptions.options.context : 
     path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);

    关于 webpack.config.js 更详尽的配置可以参考 Webpack Configuration

    开发环境

    当项目逐渐变大,webpack 的编译时间会变长,可以通过参数让编译的输出内容带有进度和颜色。

    $ webpack --progress --colors

    如果不想每次修改模块后都重新编译,那么可以启动监听模式。开启监听模式后,没有变化的模块会在编译后缓存到内存中,而不会每次都被重新编译,所以监听模式的整体速度是很快的。

    $ webpack --progress --colors --watch

    当然,使用 webpack-dev-server 开发服务是一个更好的选择。它将在 localhost:8080 启动一个 express 静态资源 web 服务器,并且会以监听模式自动运行 webpack,在浏览器打开 http://localhost:8080/ 或 http://localhost:8080/webpack... 可以浏览项目中的页面和编译后的资源输出,并且通过一个 socket.io 服务实时监听它们的变化并自动刷新页面。

    # 安装
    $ npm install webpack-dev-server -g
    
    # 运行
    $ webpack-dev-server --progress --colors

    React 开发环境的配置

    Webpack相关:

    $ npm install webpack -g
    $ npm install webpack-dev-server -g
    # 安装必要的 loader:
    ## 编译 JSX
    $ npm install --save-dev babel-loader
    ## CSS 文件处理
    $ npm install --save-dev css-loader style-loader
    ## React
    $ npm install --save-dev react-hot-loader

    Babel 相关:

    $ npm install --save-dev babel-core
    # 添加 ES6 支持
    $ npm install --save-dev babel-preset-es2015
    $ npm install --save-dev babel-react
    var webpack = require('webpack');
    module.exports = {
        entry: [
          'webpack/hot/only-dev-server',
          "./js/app.js"
        ],
        output: {
            path: './build',
            filename: "bundle.js"
        },
        module: {
            loaders: [
                { test: /.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ },
                { test: /.js$/, exclude: /node_modules/, loader: 'babel-loader'},
                { test: /.css$/, loader: "style!css" }
            ]
        },
        resolve:{
            extensions:['','.js','.json']
        },
        plugins: [
          new webpack.NoErrorsPlugin()
        ]
    };

    参考资料:

    webpack-dev-server

    webpack-dev-server 是一个基于 Node.js Express 框架的开发服务器,它是一个静态资源 Web 服务器,对于简单静态页面或者仅依赖于独立服务的前端页面,都可以直接使用这个开发服务器进行开发。在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。

    简单来说,webpack-dev-server就是一个小型的静态文件服务器。使用它,可以为webpack打包生成的资源文件提供Web服务。

    webpack-dev-server有两种模式支持自动刷新——iframe模式和inline模式。

    iframe模式

    在iframe模式下:页面是嵌套在一个iframe下的,在代码发生改动的时候,这个iframe会重新加载。使用iframe模式无需额外的配置,只需在浏览器输入:

    http://localhost:8080/webpack-dev-server/index.html
    

    inline模式

    在inline模式下:一个小型的webpack-dev-server客户端会作为入口文件打包,这个客户端会在后端代码改变的时候刷新页面。使用inline模式有两种方式:命令行方式和Node.js API。

    命令行方式比较简单,只需加入--line选项即可。例如:

    webpack-dev-server --inline

    使用--inline选项会自动把webpack-dev-server客户端加到webpack的入口文件配置中。
    注意:默认配置文件名称为:webpack.config.js,若要更改需要在命令行中指明。例如,

    webpack-dev-server --inline --config webpack.config.dev.js。

    若用Node.js API方式,因为webpack-dev-server没有inline:true这个配置项,所以需要手动把

    webpack-dev-server/client?http://localhost:8080

    加到配置文件的入口文件配置处。

    模块热替换

    webpac-dev-server 支持 Hot Module Replacement,即模块热替换,在前端代码变动的时候无需整个刷新页面,只把变化的部分替换掉。使用HMR功能也有两种方式:命令行方式和Node.js API。

    命令行方式同样比较简单,只需加入--line --hot选项。--hot这个选项干了一件事情,它把webpack/hot/dev-server入口点加入到了webpack配置文件中。这时访问浏览器,你会看见控制台的log信息:

    [HMR] Waiting for update signal from WDS...
    [WDS] Hot Module Replacement enabled.

    HMR前缀的信息由webpack/hot/dev-server模块产生,WDS前缀的信息由webpack-dev-server客户端产生。

    Node.js API方式需要做三个配置:

    1. 把 webpack/hot/dev-server 加入到webpack配置文件的 entry 项;

    2. 把 new webpack.HotModuleReplacementPlugin() 加入到webpack配置文件的plugins项;

    3. 把 hot:true 加入到 Webpack 配置文件的 webpack-dev-server 的配置项里面。

    devServer:{
        hot:true
    }

    注意:要使HMR功能生效,还需要做一件事情,就是要在应用热替换的模块或者根模块里面加入允许热替换的代码。否则,热替换不会生效,还是会重刷整个页面。

    if(module.hot)
        module.hot.accept();

    也可以使用一些插件去完成这个工作,例如webpack-module-hot-accept插件。不过,webpack-dev-server HMR结合react-hot-loader使用的时候,react-hot-loader会去做这个工作。综合上述,使用wepack-dev-server辅助开发,使得开发者在开发前端代码的过程中无需频繁手动刷新页面,使用HMR甚至不用等待页面刷新,确实可以给开发者带来很好的体验。

    但是,问题又来了。我要进行前后端联调的时候怎么办呢?毕竟webpack-dev-server只是一个静态文件服务器,不具备动态处理的能力。这个时候就需要将后端服务器与webpack-dev-server结合使用了。webpack-dev-server只用来为webpack打包生成的资源文件提供服务,比如js文件、图片文件、css文件等;后端服务器除提供API接口外,还提供入口HTML。

    要将webpack-dev-server与后端服务器结合使用,需要做三件事情。

    第一 首页HTML文件是从后端服务器发出的,这时页面的根地址变成了后端服务器地址,怎么使得webpack产生的资源文件在请求资源的时候是向web-dev-server请求而不是后端服务器请求?只需在webpack配置文件中的 output.publicPath 配置项写上绝对URL地址,例如output.publicPath = "http://localhost:8080/assets/"。这时,webpack打包产生的资源文件里面的url地址都会是绝对地址,而不是相对地址。
    第二 后端服务器产生的入口HTML文件要向webpack-dev-server请求资源文件,这个简单,只需在HTML文件中加入资源文件的绝对地址,例如:<script src="http://localhost:8080/assets/bundle.js">
    第三 要使webpack-dev-server和它的运行时程序连接起来。这个简单,只需要使用iline模式即可。

    提取公共代码与压缩

    var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
    module.exports = {
        ... ...
        // plugins 项配置中增加
        plugins: [
        
            ... ...  
            // 提取公共代码
            commonsPlugin,
            //压缩
            new webpack.optimize.UglifyJsPlugin({
                    compress: {
                        warnings: false
                    }
            })
        ]
    }

    参见:
    WEBPACK DEV SERVER
    webpack-dev-server 官方文档
    前端模块加载工具——webpack(二)

  • 相关阅读:
    ceph中pool的管理
    ubuntu 16.04安装ceph集群(双节点)
    win7设置开机启动virtualBOX虚拟机
    Linux Linux程序练习十九
    Linux Linux程序练习十八
    Linux Linux程序练习十七
    Linux 网络编程详解十二
    Linux socket多进程服务器框架三
    Linux socket多进程服务器框架二
    Linux socket多进程服务器框架一
  • 原文地址:https://www.cnblogs.com/sunshq/p/6225001.html
Copyright © 2011-2022 走看看