zoukankan      html  css  js  c++  java
  • webpack工程搭建

    >>建立nodejs工程

    新建文件夹,npm init 生成package.json

    >>安装webpack 和 webpack-dev-server

    npm install --save-dev webpack@3.8.1 注意4.x版本语法有些变化

    npm install --save-dev webpack-dev-server@2.9.7 注意踩坑记录1

    >>安装babel转码es6

    Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

    babel 6 与 bable-loader 7匹配,

    另外解决promise等,用 babel-polyfill,bebel6对应版本为babel-polyfill 6,解决组件按需加载编译,用 babel-plugin-import

    >>支持 react 开发

    npm install --save-dev react react-dom 注意这里是本地安装,也可以用全局安装

    安装其他可选插件:

    html-webpack-plugin
    clean-webpack-plugin
    copy-webpack-plugin

    >>配置webpack.config.js

    踩坑记录

    1:webpack是3.x版本的,webpack-dev-server是3.x的版本,这两个版本不兼容,可以把webpack-dev-server降到2.x版本

    踩坑解决办法示例:TypeError: Cannot read property 'compile' of undefined #1334

    解决思路: 优先使用 Google 引擎进行搜索关键词句, 比如 webpack Cannot read property 'compile' of undefined;看能否找到相应的问题。

    如果不行,不妨换一种方式再搜索,譬如:site:stackoverflow.com webpack Cannot read property 'compile' of undefined,在具体某个网站下搜索;如果还是没能找见解决办法的话,可以在各种平台提问,比如 segmentfault。

    额外补充: 对于 Google 这个工具还真是有必要先学,具体常用操作可参见:如何更好地运用 Chrome (Google)。倘若,不能够没有适用 Google 的环境,那么这里整理集结若干优质搜索引擎,堪称 Google 搜索优质替代品,可供参考。

    package.json 示例:

    {
      "name": "webpack",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo "Error: no test specified" && exit 1",
        "build": "webpack --config webpack.config.js --watch"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "antd": "^3.25.0",
        "babel-core": "^6.26.3",
        "babel-loader": "^7.1.5",
        "babel-plugin-import": "^1.12.2",
        "babel-plugin-transform-class-properties": "^6.24.1",
        "babel-plugin-transform-runtime": "^6.23.0",
        "babel-polyfill": "^6.26.0",
        "babel-preset-env": "^1.7.0",
        "babel-preset-react": "^6.24.1",
        "babel-preset-stage-0": "^6.24.1",
        "clean-webpack-plugin": "^1.0.0",
        "copy-webpack-plugin": "^4.6.0",
        "css-loader": "^3.2.0",
        "extract-text-webpack-plugin": "^3.0.2",
        "file-loader": "^4.2.0",
        "html-webpack-plugin": "^3.2.0",
        "less-loader": "^5.0.0",
        "sass-loader": "^7.3.1",
        "style-loader": "^1.0.0",
        "uglifyjs-webpack-plugin": "^1.3.0",
        "url-loader": "^2.1.0",
        "webpack": "^3.8.1",
        "webpack-bundle-analyzer": "^3.1.0",
        "webpack-dev-server": "^2.9.7"
      },
      "dependencies": {
        "react": "^16.9.0",
        "react-dom": "^16.9.0"
      }
    }

    webpack.config.js 示例:

    const path = require("path");
    const webpack = require("webpack");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const CopyWebpackPlugin = require("copy-webpack-plugin");
    const CleanWebpackPlugin = require("clean-webpack-plugin");
    // const ExtractTextPlugin = require("extract-text-webpack-plugin");
    // const UglifyJsPlugin  = require('uglifyjs-webpack-plugin');
    
    module.exports = {
      // context: path.resolve(__dirname), //webpack运行时的根目录,默认为当前目录
    
      // 配置寻找模块的规则
      resolve: {
        modules: [ // 寻找模块的根目录,array 类型,默认以 node_modules 为根目录
          path.resolve(__dirname),
          'node_modules'
        ],
        extensions: ['.js', '.jsx', '.json', '.css', '.less', '.scss'], // 当模块没有后缀名时,webpack尝试添加后查找文件,常用的放前面
        alias: { // 别名,用于替换import导入模块的路径,例如可在dev环境导入不压缩的包,prd环境导入压缩的包
          "react": "react/cjs/react.production.min.js", // 将import react替换为import "react.min.js"
          "react-dom": "react-dom/cjs/react-dom.production.min.js",
          // "LP": "xxx/lp.min.js", // 将"LP"替换为xxx/lp.min.js
          // 'only-module$': 'new-module'
          // 使用结尾符号 $ 后,只把 'only-module'结尾的路径 映射成 'new-module',
          // 'module/path/file' 不会被映射成 'new-module/path/file'
        },
        enforceExtension: false, // 是否强制导入语句必须要写明文件后缀
      },
    
      // entry 表示入口文件,注意这里的路径拼上resolve.modules路径即尝试加载入口文件的完整路径
      // 类型可以是 string | object | array   
      // entry: "src/js/entry.js", // 只有1个入口,入口只有1个文件,对应1个chunk出口文件,且命名为main
      // entry: ["src/js/entry1.js", "src/js/entry2.js"], // 只有1个入口,入口有2个文件,对应1个chunk出口文件,且命名为main
      entry: { // 有2个入口,对应2个chunk出口文件。entry为object时对应多个chunk出口文件,名称默认为key值。
        // 'vendor': ['babel-polyfill', 'isomorphic-fetch', 'prop-types', 'react', 'react-dom', 'react-redux', 'react-router-dom', 'redux', 'react-bootstrap'],
        'vendor': ['babel-polyfill', 'react', 'react-dom'],
        "index": "src/app/index.js"
      },
      // entry: ()=>{ // 可由函数动态生成entry
      //   return {
      //     a:"xxx",
      //     b:"yyy"
      //   }
      // }
    
      // resolveLoader: { // 告诉 webpack 如何寻找Loader源文件,可用于加载本地的Loader
      //   modules: ['node_modules'], // 去哪个目录寻找
      //   extensions: ['.js', 'json'], // 入口文件后缀
      //   mainFields: ['loader', 'main'] // 优先使用哪种类型的入口,可不配置
      // },
    
      externals: { // 告诉webpack要构建的代码中使用了哪些不用打包的模块(通常直接在模板html的script标签中引入),即直接使用JS运行环境提供的全局变量
        jquery: 'window.jQuery' // 将导入语句里的jqurey替换成全局变量 window.jQuery。例如代码中有 import $ from 'jquery' 会被正确的替换
      }, // 一般代码中也不会用模块引入的语法引入本身不打包的模块,因此这一项也可以不配置
    
      // 如何输出结果:在 Webpack 经过一系列处理后,如何输出最终想要的代码。
      output: {
        // 输出文件存放的目录,必须是 string 类型的绝对路径。
        path: path.resolve(__dirname, 'dist'),
        // 引用资源的路径 URL 前缀,拼上entry的路径即为资源访问路径。
        // publicPath: 'https://cdn.example.com/', // 放到 CDN 上去,html中引入js时使用https://cdn.example.com/xxx.js
        // 通常本地调试和发布到线上时访问路径不同,可以根据参数化选择:
        // publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath
    
        // 输出文件的名称
        // filename: 'bundle.js', // 完整的名称
        // filename: '[name].js', // 当配置了多个 entry 时,通过名称模版为不同的 entry 生成不同的文件名称
        filename: '[name].[chunkHash:8].js', // 根据文件内容 hash 值取8位生成文件名称,用于浏览器长时间缓存文件
    
    
        // 导出库的名称,string 类型
        // 不填它时,默认输出格式是匿名的立即执行函数
        // library: 'MyLibrary',
    
        // 导出库的类型,枚举类型,默认是 var
        // 可以是 umd | umd2 | commonjs2 | commonjs | amd | this | var | assign | window | global | jsonp ,
        // libraryTarget: 'umd', 
    
        // 附加 Chunk 的文件名称,用于指定无入口、动态加载、webpack运行过程中生成的chunk的名称,通常也可以在具体的plugin中去配置
        // chunkFilename: '[id].js',
        // chunkFilename: '[chunkhash].js'
      },
    
      // 配置模块相关
      module: {
        rules: [ // 配置 Loader
          {
            test: /.jsx?$/, // 正则匹配命中要使用该 Loader 的文件,可以为字符串或正则,或者数组类型
            // include: [ // 匹配范围包含,即只会命中这里面的文件
            //   path.resolve(__dirname, 'src')
            // ],
            exclude: /node_modules/, // 匹配范围不包含,即排除这里面的文件
            use: [{ // 使用哪些 Loader,有多个时从后往前执行,可以通过options传一些参数
              loader: "babel-loader"
            }],
          },
          {
            test: /.css$/,
            use: ['style-loader', 'css-loader'] // 不传参数时可以简写
            // 注:style-loader作用是将css编译到js中,然后运行时插入DOM节点的style属性,可能导致js代码较大,且css复用性不够。可用ExtractTextWebpackPlugin优化。
            // use: ExtractTextPlugin.extract({
            //   fallback: "style-loader", // 编译后用什么loader来提取css文件并引入到html
            //   use: "css-loader"
            // })
          },
          {
            test: /.less$/,
            use:['style-loader','css-loader', 'less-loader']
          },
          {
            test: /.scss$/,
            use:['style-loader','css-loader', 'sass-loader']
          },
          {
            // 对小图片或字体文件直接编码为base64提高加载速度,当图片超过限制时直接使用file-loader拷贝
            test: /.(png|jpe?g|gif|svg|eot|woff2?|ttf|pdf)$/,
            loader: 'url-loader',
            include: [path.resolve(__dirname, 'src')], 
            options: {
              limit: 10000, // 小于10kb的才编码为base64,建议10kb量级的小文件才编码
              name: 'media/[name].[hash:7].[ext]'
              // 另外还可以配置publicPath等参数,参见文末说明
            }
          },
        ],
        noParse: [ // 不用解析和处理的模块,如jQuery等非模块化的库,被忽略的模块不能包含import require define等模块化语句
          /jquery|chartjs/  // 用正则匹配
        ],
        // noParse: (path) => {
        //   return /jquery|chartjs/.test(path);
        // }
      },
    
      plugins: [
        new CleanWebpackPlugin(['dist']),
        new CopyWebpackPlugin([{
          from: __dirname+'/src/resource', // 静态资源目录地址
          to: __dirname+'/dist/resource',
          ignore: ["test.html"]
        }]),
        // new ExtractTextPlugin({ // 提取js的css到文件,参见文末说明
        //   filename: 'css/style.[contenthash:16].css',
        // }),
        new webpack.optimize.CommonsChunkPlugin({ // 从vendor chunk中提取第三方公共模块,然后从中提取webpack运行文件到runtime
          name: ['vendor','runtime'],
          filename: '[name].[hash:7].js',
          minChunks: 2 // 公共模块被引用几次时被提取出来
        }),
        // new webpack.optimize.CommonsChunkPlugin({ // 提取自定义公共模块
        //     name: 'common',
        //     filename: '[name].[hash:7].js',
        //     chunks: ['first','second']//从first和second chunk中抽取commons chunk
        // }),
        // new UglifyJsPlugin(),
        new HtmlWebpackPlugin({
          title: "首页", // 指定页面标题
          favicon: "", // 指定页面图标
          template: __dirname + "/src/template/index.html", // html模板文件路径
          inject: true, // true|head|body|false,当传入 true或者 body时所有js模块将被放置在body元素的底部,head时则会放在head元素内
          // minify:{    // 压缩HTML文件
          //   removeComments:true,    // 移除HTML中的注释
          //   collapseWhitespace:true    // 删除空白符与换行符
          // },
          filename: "index.html", // 输出html文件路径
          chunks: [ // 向html script标签中添加哪些chunk
            "runtime", // webpack运行文件要排在最前面优先加载
            "vendor",
            "index"        
          ],
          // excludeChunks: [ // 被排除的模块
          //   "main"
          // ]
          chunksSortMode: 'manual', // 设置chunk插入到html的script标签的顺序, none|auto|dependency|manual|{function} 默认为'auto,常用manual根据chunks的位置手动排序
        }),
      ],
      devServer: {
        contentBase: path.join(__dirname, "dist"), //本地服务器所加载的页面所在的目录
        publicPath: "/dist/", // 和output.publicPath作用一样,本地调试用,会覆盖output.publicPath的值,确保以/开头以/结尾
        inline: true, // 实时刷新
        // hot: true, // 是否开启模块热替换功能(不重新加载整个网页的情况下刷新模块),默认关闭
        port: 9000, // 端口改为9000
        compress: true, // 是否开启 gzip 压缩
        https: false, // 是否开启 HTTPS 模式
        // open:true // 自动打开浏览器
        // headers: { // response中注入一些响应头
        //   'X-foo': '112233'
        // },
        // historyApiFallback: true, // 404时定向跳转,一应用在HTML5 History API 的单页应用,比如访问不到路由时跳转到index.html
        // 还可以配置proxy 实现跨域请求,参见文末
      },
      // http://localhost:9000/webpack-dev-server 可以查看运行时dev-server在内存中生成的目录结构
    
      // 其他配置
      devtool: 'source-map', // 配置 source-map 类型,方便调试时查看源码
      profile: false, // 是否捕捉 Webpack 构建的性能信息,用于分析什么原因导致构建性能不佳
      cache: true, // 是否启用缓存提升构建速度
      watch: true, // 是否开启监听模式,在文件变化时自动重新编译(默认否,当使用devServer时默认开启)
      watchOptions: { // 监听模式选项
        // 不监听的文件或文件夹,支持正则匹配。默认为空
        ignored: /node_modules/,
        // 监听到变化发生后会等待多长时间再去执行编译,防止文件更新太快导致重新编译频率太高,默认为300ms 
        aggregateTimeout: 300,
        // 检测文件是否发生变化的间隔时长,默认每隔1000毫秒询问一次
        poll: 1000
      },
      
      // 输出文件性能检查配置
      performance: { 
        hints: 'warning', // 有性能问题时输出警告
        hints: 'error', // 有性能问题时输出错误
        hints: false, // 关闭性能检查
        maxAssetSize: 200000, // 最大文件大小 (单位 bytes)
        maxEntrypointSize: 400000, // 最大入口文件大小 (单位 bytes)
        assetFilter: function(assetFilename) { // 过滤要检查的文件
          return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
        }
      },
    
      stats: { // 控制台输出日志控制
        assets: true,
        colors: true,
        errors: true,
        errorDetails: true,
        hash: true,
      }
      
    };
    
    
    // node中的路径
    // __dirname: 总是返回被执行的 js 所在文件夹的绝对路径
    // __filename: 总是返回被执行的 js 的绝对路径
    // process.cwd(): 总是返回运行 node 命令时所在的文件夹的绝对路径
    
    /*
    devServer proxy 实现跨域
    
    有时候我们使用webpack在本地启动服务器的时候,由于我们使用的访问的域名是 http://localhost:8081 这样的,但是我们服务端的接口是其他的,
    那么就存在域名或端口号跨域的情况下,但是很幸运的是 devServer有一个叫proxy配置项,可以通过该配置来解决跨域的问题,那是因为 dev-server 使用了 http-proxy-middleware 包(了解该包的更多用法 )。
    假如现在我们本地访问的域名是 http://localhost:8081, 但是我现在调用的是百度页面中的一个接口,该接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。现在我们只需要在devServer中的proxy的配置就可以了:
    如下配置:
    proxy: {
      '/api': {
        target: 'http://news.baidu.com', // 目标接口的域名
        // secure: true,  // https 的时候 使用该参数
        changeOrigin: true,  // 是否跨域
        pathRewrite: {
          '^/api' : ''  // 重写路径
        }
      }
    }
    然后我们在main.js里面编写如下代码:
    import axios from 'axios';
    axios.get('/api/widget?ajax=json&id=ad').then(res => {
      console.log(res);
    });
    在这里请求我使用 axios 插件,其实和jquery是一个意思的。为了方便就用了这个。
    下面我们来理解下上面配置的含义:
    1. 首先是百度的接口地址是这样的:http://news.baidu.com/widget?ajax=json&id=ad;
    2. proxy 的配置项 '/api' 和 target: 'http://news.baidu.com' 的含义是,匹配请求中 /api 含有这样的域名重定向 到 'http://news.baidu.com'来。
    因此我在接口地址上 添加了前缀 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 因此会自动补充前缀,也就是说,url: '/api/widget?ajax=json&id=ad' 等价
    于 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'.
    3. changeOrigin: true/false 还参数值是一个布尔值,含义是 是否需要跨域。
    4. secure: true, 如果是https请求就需要改参数配置,需要ssl证书吧。
    5. pathRewrite: {'^/api' : ''}的含义是重写url地址,把url的地址里面含有 '/api' 这样的 替换成 '', 
    因此接口地址就变成了 http://news.baidu.com/widget?ajax=json&id=ad; 因此就可以请求得到了,最后就返回
    接口数据了。
    */
    
    /* https://segmentfault.com/a/1190000018987483?utm_source=tag-newest
    url-loader 会将引入的图片(还有字体、音视频等)编码,生成dataURl并将其打包到文件中,引入这个dataURL就能访问。如果图片较大,编码会消耗性能。
    因此url-loader提供了一个limit参数,小于limit字节的文件会被转为DataURl,大于limit的内部调用file-loader进行copy。
    接下来摘取几个重要的属性做说明
    
    outputPath
    该属性指明我们最终导出的文件路径
    最终导出的文件路径 === output.path + url-loader.outputPath + url-loader.name
    
    publicPath(常用于生成环境)
    该属性指明我们最终引用的文件路径(打包生成的index.html文件里面引用资源的前缀)
    最终引用的文件路径前缀 === output.publicPath + url-loader.publicPath + url-loader.name
    
    name
    该属性指明文件的最终名称。
    同样的,我们可以直接把outputPath的内容写到name中,一样可以生成对应的路径
    经过上面的说明,我们得出结论,最终的静态文件路径(图片,音频,视频,字体等)=== output.publicPath + url-loader.publicPath + output.path + url-loader.outputPath + url-loader.name
    
    我们在本地开发的时候都是localhost:8080/下面的根目录,所以当图片生成如下的绝对地址是不会出问题的,可是同样的webpack配置放到生成环境上就不一定了。
    因为生成环境大部分的前端静态文件都不是在根目录啊,有可能就是这样的目录结构
    www/
     +folder/
       +static/
         +css/
         +img/
         +js/
       +index.html
    这样你开发环境的绝对路径放到服务器上面自然就404了,所以要不然用相对路径,要不然就统一写死绝对路径(针对不同环境添加不同的publicPath)
    (同样道理,解释为什么css里面的背景图路径404,但是这个解决起来需要用到extract-text-webpack-plugin或者mini-css-extract-plugin这个插件,前者用于webpack4以下版本,后者是4以上版本,配置options里面的publicPath)
    
    css 提取插件,`extract-text-webpack-plugin` 插件 或者 `mini-css-extract-plugin` 的 loader也都提供了publicPath,用来复写提取出来的css文件中的资源的引入路径
    */

    模板html示例:

    <!DOCTYPE html>
    <head>
        <title><%=htmlWebpackPlugin.options.title %></title>
        <meta charset="utf-8" />
        <style>
            /* 垂直居中显示 */
            .center-in-center{
                position: absolute;
                margin: 10px;
                padding: 10px;
                top: 50%;
                left: 50%;
                -webkit-transform: translate(-50%, -50%);
                -moz-transform: translate(-50%, -50%);
                -ms-transform: translate(-50%, -50%);
                -o-transform: translate(-50%, -50%);
                transform: translate(-50%, -50%);
            }
        </style>
    </head>
    <body>
        <div id="root" >
        </div>
    </body>
    </html>

    解释下版本号:

    1.15.2对应就是MAJOR,MINOR.PATCH:1是marjor version;15是minor version;2是patch version。
    MAJOR:这个版本号变化了表示有了一个不可以和上个版本兼容的大更改。
    MINOR:这个版本号变化了表示有了增加了新的功能,并且可以向后兼容。
    PATCH:这个版本号变化了表示修复了bug,并且可以向后兼容。

    当我们使用最新的Node运行‘npm instal --save xxx',的时候,他会优先考虑使用插入符号(^)而不是波浪符号(~)了。

    波浪符号(~):他会更新到当前minor version(也就是中间的那位数字)中最新的版本。放到我们的例子中就是:body-parser:~1.15.2,这个库会去匹配更新到1.15.x的最新版本,如果出了一个新的版本为1.16.0,则不会自动升级。波浪符号是曾经npm安装时候的默认符号,现在已经变为了插入符号。

    插入符号(^):这个符号就显得非常的灵活了,他将会把当前库的版本更新到当前major version(也就是第一位数字)中最新的版本。放到我们的例子中就是:bluebird:^3.3.4,这个库会去匹配3.x.x中最新的版本,但是他不会自动更新到4.0.0。

    因为major version变化表示可能会影响之前版本的兼容性,所以无论是波浪符号还是插入符号都不会自动去修改major version,因为这可能导致程序crush,可能需要手动修改代码。

    const path = require("path");
    const webpack = require("webpack");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const CopyWebpackPlugin = require("copy-webpack-plugin");
    const CleanWebpackPlugin = require("clean-webpack-plugin");
    // const ExtractTextPlugin = require("extract-text-webpack-plugin");
    // const UglifyJsPlugin  = require('uglifyjs-webpack-plugin');

    module.exports = {
      // context: path.resolve(__dirname), //webpack运行时的根目录,默认为当前目录

      // 配置寻找模块的规则
      resolve: {
        modules: [ // 寻找模块的根目录,array 类型,默认以 node_modules 为根目录
          path.resolve(__dirname),
          'node_modules'
        ],
        extensions: ['.js''.jsx''.json''.css''.less''.scss'], // 当模块没有后缀名时,webpack尝试添加后查找文件,常用的放前面
        alias: { // 别名,用于替换import导入模块的路径,例如可在dev环境导入不压缩的包,prd环境导入压缩的包
          "react": "react/cjs/react.production.min.js"// 将import react替换为import "react.min.js"
          "react-dom": "react-dom/cjs/react-dom.production.min.js",
          // "LP": "xxx/lp.min.js", // 将"LP"替换为xxx/lp.min.js
          // 'only-module$': 'new-module'
          // 使用结尾符号 $ 后,只把 'only-module'结尾的路径 映射成 'new-module',
          // 'module/path/file' 不会被映射成 'new-module/path/file'
        },
        enforceExtension: false// 是否强制导入语句必须要写明文件后缀
      },

      // entry 表示入口文件,注意这里的路径拼上resolve.modules路径即尝试加载入口文件的完整路径
      // 类型可以是 string | object | array   
      // entry: "src/js/entry.js", // 只有1个入口,入口只有1个文件,对应1个chunk出口文件,且命名为main
      // entry: ["src/js/entry1.js", "src/js/entry2.js"], // 只有1个入口,入口有2个文件,对应1个chunk出口文件,且命名为main
      entry: { // 有2个入口,对应2个chunk出口文件。entry为object时对应多个chunk出口文件,名称默认为key值。
        // 'vendor': ['babel-polyfill', 'isomorphic-fetch', 'prop-types', 'react', 'react-dom', 'react-redux', 'react-router-dom', 'redux', 'react-bootstrap'],
        'vendor': ['babel-polyfill''react''react-dom'],
        "index": "src/app/index.js"
      },
      // entry: ()=>{ // 可由函数动态生成entry
      //   return {
      //     a:"xxx",
      //     b:"yyy"
      //   }
      // }

      // resolveLoader: { // 告诉 webpack 如何寻找Loader源文件,可用于加载本地的Loader
      //   modules: ['node_modules'], // 去哪个目录寻找
      //   extensions: ['.js', 'json'], // 入口文件后缀
      //   mainFields: ['loader', 'main'] // 优先使用哪种类型的入口,可不配置
      // },

      externals: { // 告诉webpack要构建的代码中使用了哪些不用打包的模块(通常直接在模板html的script标签中引入),即直接使用JS运行环境提供的全局变量
        jquery: 'window.jQuery' // 将导入语句里的jqurey替换成全局变量 window.jQuery。例如代码中有 import $ from 'jquery' 会被正确的替换
      }, // 一般代码中也不会用模块引入的语法引入本身不打包的模块,因此这一项也可以不配置

      // 如何输出结果:在 Webpack 经过一系列处理后,如何输出最终想要的代码。
      output: {
        // 输出文件存放的目录,必须是 string 类型的绝对路径。
        path: path.resolve(__dirname'dist'),
        // 引用资源的路径 URL 前缀,拼上entry的路径即为资源访问路径。
        // publicPath: 'https://cdn.example.com/', // 放到 CDN 上去,html中引入js时使用https://cdn.example.com/xxx.js
        // 通常本地调试和发布到线上时访问路径不同,可以根据参数化选择:
        // publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath

        // 输出文件的名称
        // filename: 'bundle.js', // 完整的名称
        // filename: '[name].js', // 当配置了多个 entry 时,通过名称模版为不同的 entry 生成不同的文件名称
        filename: '[name].[chunkHash:8].js'// 根据文件内容 hash 值取8位生成文件名称,用于浏览器长时间缓存文件


        // 导出库的名称,string 类型
        // 不填它时,默认输出格式是匿名的立即执行函数
        // library: 'MyLibrary',

        // 导出库的类型,枚举类型,默认是 var
        // 可以是 umd | umd2 | commonjs2 | commonjs | amd | this | var | assign | window | global | jsonp ,
        // libraryTarget: 'umd', 

        // 附加 Chunk 的文件名称,用于指定无入口、动态加载、webpack运行过程中生成的chunk的名称,通常也可以在具体的plugin中去配置
        // chunkFilename: '[id].js',
        // chunkFilename: '[chunkhash].js'
      },

      // 配置模块相关
      module: {
        rules: [ // 配置 Loader
          {
            test: /.jsx?$/// 正则匹配命中要使用该 Loader 的文件,可以为字符串或正则,或者数组类型
            // include: [ // 匹配范围包含,即只会命中这里面的文件
            //   path.resolve(__dirname, 'src')
            // ],
            exclude: /node_modules/// 匹配范围不包含,即排除这里面的文件
            use: [{ // 使用哪些 Loader,有多个时从后往前执行,可以通过options传一些参数
              loader: "babel-loader"
            }],
          },
          {
            test: /.css$/,
            use: ['style-loader''css-loader'// 不传参数时可以简写
            // 注:style-loader作用是将css编译到js中,然后运行时插入DOM节点的style属性,可能导致js代码较大,且css复用性不够。可用ExtractTextWebpackPlugin优化。
            // use: ExtractTextPlugin.extract({
            //   fallback: "style-loader", // 编译后用什么loader来提取css文件并引入到html
            //   use: "css-loader"
            // })
          },
          {
            test: /.less$/,
            use:['style-loader','css-loader''less-loader']
          },
          {
            test: /.scss$/,
            use:['style-loader','css-loader''sass-loader']
          },
          {
            // 对小图片或字体文件直接编码为base64提高加载速度,当图片超过限制时直接使用file-loader拷贝
            test: /.(png|jpe?g|gif|svg|eot|woff2?|ttf|pdf)$/,
            loader: 'url-loader',
            include: [path.resolve(__dirname'src')], 
            options: {
              limit: 10000// 小于10kb的才编码为base64,建议10kb量级的小文件才编码
              name: 'media/[name].[hash:7].[ext]'
              // 另外还可以配置publicPath等参数,参见文末说明
            }
          },
        ],
        noParse: [ // 不用解析和处理的模块,如jQuery等非模块化的库,被忽略的模块不能包含import require define等模块化语句
          /jquery|chartjs/  // 用正则匹配
        ],
        // noParse: (path) => {
        //   return /jquery|chartjs/.test(path);
        // }
      },

      plugins: [
        new CleanWebpackPlugin(['dist']),
        new CopyWebpackPlugin([{
          from: __dirname+'/src/resource'// 静态资源目录地址
          to: __dirname+'/dist/resource',
          ignore: ["test.html"]
        }]),
        // new ExtractTextPlugin({ // 提取js的css到文件,参见文末说明
        //   filename: 'css/style.[contenthash:16].css',
        // }),
        new webpack.optimize.CommonsChunkPlugin({ // 从vendor chunk中提取第三方公共模块,然后从中提取webpack运行文件到runtime
          name: ['vendor','runtime'],
          filename: '[name].[hash:7].js',
          minChunks: 2 // 公共模块被引用几次时被提取出来
        }),
        // new webpack.optimize.CommonsChunkPlugin({ // 提取自定义公共模块
        //     name: 'common',
        //     filename: '[name].[hash:7].js',
        //     chunks: ['first','second']//从first和second chunk中抽取commons chunk
        // }),
        // new UglifyJsPlugin(),
        new HtmlWebpackPlugin({
          title: "首页"// 指定页面标题
          favicon: ""// 指定页面图标
          template: __dirname + "/src/template/index.html"// html模板文件路径
          inject: true// true|head|body|false,当传入 true或者 body时所有js模块将被放置在body元素的底部,head时则会放在head元素内
          // minify:{    // 压缩HTML文件
          //   removeComments:true,    // 移除HTML中的注释
          //   collapseWhitespace:true    // 删除空白符与换行符
          // },
          filename: "index.html"// 输出html文件路径
          chunks: [ // 向html script标签中添加哪些chunk
            "runtime"// webpack运行文件要排在最前面优先加载
            "vendor",
            "index"        
          ],
          // excludeChunks: [ // 被排除的模块
          //   "main"
          // ]
          chunksSortMode: 'manual'// 设置chunk插入到html的script标签的顺序, none|auto|dependency|manual|{function} 默认为'auto,常用manual根据chunks的位置手动排序
        }),
      ],
      devServer: {
        contentBase: path.join(__dirname"dist"), //本地服务器所加载的页面所在的目录
        publicPath: "/dist/"// 和output.publicPath作用一样,本地调试用,会覆盖output.publicPath的值,确保以/开头以/结尾
        inline: true// 实时刷新
        // hot: true, // 是否开启模块热替换功能(不重新加载整个网页的情况下刷新模块),默认关闭
        port: 9000// 端口改为9000
        compress: true// 是否开启 gzip 压缩
        https: false// 是否开启 HTTPS 模式
        // open:true // 自动打开浏览器
        // headers: { // response中注入一些响应头
        //   'X-foo': '112233'
        // },
        // historyApiFallback: true, // 404时定向跳转,一应用在HTML5 History API 的单页应用,比如访问不到路由时跳转到index.html
        // 还可以配置proxy 实现跨域请求,参见文末
      },
      // http://localhost:9000/webpack-dev-server 可以查看运行时dev-server在内存中生成的目录结构

      // 其他配置
      devtool: 'source-map'// 配置 source-map 类型,方便调试时查看源码
      profile: false// 是否捕捉 Webpack 构建的性能信息,用于分析什么原因导致构建性能不佳
      cache: false// 是否启用缓存提升构建速度
      watch: true// 是否开启监听模式,在文件变化时自动重新编译(默认否,当使用devServer时默认开启)
      watchOptions: { // 监听模式选项
        // 不监听的文件或文件夹,支持正则匹配。默认为空
        ignored: /node_modules/,
        // 监听到变化发生后会等待多长时间再去执行编译,防止文件更新太快导致重新编译频率太高,默认为300ms 
        aggregateTimeout: 300,
        // 检测文件是否发生变化的间隔时长,默认每隔1000毫秒询问一次
        poll: 1000
      },
      
      // 输出文件性能检查配置
      performance: { 
        hints: 'warning'// 有性能问题时输出警告
        hints: 'error'// 有性能问题时输出错误
        hints: false// 关闭性能检查
        maxAssetSize: 200000// 最大文件大小 (单位 bytes)
        maxEntrypointSize: 400000// 最大入口文件大小 (单位 bytes)
        assetFilter: function(assetFilename) { // 过滤要检查的文件
          return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
        }
      },

      stats: { // 控制台输出日志控制
        assets: true,
        colors: true,
        errors: true,
        errorDetails: true,
        hash: true,
      }
      
    };


    // node中的路径
    // __dirname: 总是返回被执行的 js 所在文件夹的绝对路径
    // __filename: 总是返回被执行的 js 的绝对路径
    // process.cwd(): 总是返回运行 node 命令时所在的文件夹的绝对路径

    /*
    devServer proxy 实现跨域

    有时候我们使用webpack在本地启动服务器的时候,由于我们使用的访问的域名是 http://localhost:8081 这样的,但是我们服务端的接口是其他的,
    那么就存在域名或端口号跨域的情况下,但是很幸运的是 devServer有一个叫proxy配置项,可以通过该配置来解决跨域的问题,那是因为 dev-server 使用了 http-proxy-middleware 包(了解该包的更多用法 )。
    假如现在我们本地访问的域名是 http://localhost:8081, 但是我现在调用的是百度页面中的一个接口,该接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。现在我们只需要在devServer中的proxy的配置就可以了:
    如下配置:
    proxy: {
      '/api': {
        target: 'http://news.baidu.com', // 目标接口的域名
        // secure: true,  // https 的时候 使用该参数
        changeOrigin: true,  // 是否跨域
        pathRewrite: {
          '^/api' : ''  // 重写路径
        }
      }
    }
    然后我们在main.js里面编写如下代码:
    import axios from 'axios';
    axios.get('/api/widget?ajax=json&id=ad').then(res => {
      console.log(res);
    });
    在这里请求我使用 axios 插件,其实和jquery是一个意思的。为了方便就用了这个。
    下面我们来理解下上面配置的含义:
    1. 首先是百度的接口地址是这样的:http://news.baidu.com/widget?ajax=json&id=ad;
    2. proxy 的配置项 '/api' 和 target: 'http://news.baidu.com' 的含义是,匹配请求中 /api 含有这样的域名重定向 到 'http://news.baidu.com'来。
    因此我在接口地址上 添加了前缀 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 因此会自动补充前缀,也就是说,url: '/api/widget?ajax=json&id=ad' 等价
    于 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'.
    3. changeOrigin: true/false 还参数值是一个布尔值,含义是 是否需要跨域。
    4. secure: true, 如果是https请求就需要改参数配置,需要ssl证书吧。
    5. pathRewrite: {'^/api' : ''}的含义是重写url地址,把url的地址里面含有 '/api' 这样的 替换成 '', 
    因此接口地址就变成了 http://news.baidu.com/widget?ajax=json&id=ad; 因此就可以请求得到了,最后就返回
    接口数据了。
    */

    /* https://segmentfault.com/a/1190000018987483?utm_source=tag-newest
    url-loader 会将引入的图片(还有字体、音视频等)编码,生成dataURl并将其打包到文件中,引入这个dataURL就能访问。如果图片较大,编码会消耗性能。
    因此url-loader提供了一个limit参数,小于limit字节的文件会被转为DataURl,大于limit的内部调用file-loader进行copy。
    接下来摘取几个重要的属性做说明

    outputPath
    该属性指明我们最终导出的文件路径
    最终导出的文件路径 === output.path + url-loader.outputPath + url-loader.name

    publicPath(常用于生成环境)
    该属性指明我们最终引用的文件路径(打包生成的index.html文件里面引用资源的前缀)
    最终引用的文件路径前缀 === output.publicPath + url-loader.publicPath + url-loader.name

    name
    该属性指明文件的最终名称。
    同样的,我们可以直接把outputPath的内容写到name中,一样可以生成对应的路径
    经过上面的说明,我们得出结论,最终的静态文件路径(图片,音频,视频,字体等)=== output.publicPath + url-loader.publicPath + output.path + url-loader.outputPath + url-loader.name

    我们在本地开发的时候都是localhost:8080/下面的根目录,所以当图片生成如下的绝对地址是不会出问题的,可是同样的webpack配置放到生成环境上就不一定了。
    因为生成环境大部分的前端静态文件都不是在根目录啊,有可能就是这样的目录结构
    www/
     +folder/
       +static/
         +css/
         +img/
         +js/
       +index.html
    这样你开发环境的绝对路径放到服务器上面自然就404了,所以要不然用相对路径,要不然就统一写死绝对路径(针对不同环境添加不同的publicPath)
    (同样道理,解释为什么css里面的背景图路径404,但是这个解决起来需要用到extract-text-webpack-plugin或者mini-css-extract-plugin这个插件,前者用于webpack4以下版本,后者是4以上版本,配置options里面的publicPath)

    css 提取插件,`extract-text-webpack-plugin` 插件 或者 `mini-css-extract-plugin` 的 loader也都提供了publicPath,用来复写提取出来的css文件中的资源的引入路径
    */
  • 相关阅读:
    Collection与Collections的区别
    Java容器基础概况
    IO与NIO的区别
    BIO、NIO、AIO的区别
    Java中的IO流
    Java中的抽象类
    String常用方法解析
    字符串反转
    vue路由传参
    flask框架(八)—自定义命令flask-script、多app应用、wtforms表单验证、SQLAIchemy
  • 原文地址:https://www.cnblogs.com/ccdat/p/10192400.html
Copyright © 2011-2022 走看看