>>建立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: , // 小于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:  // 公共模块被引用几次时被提取出来
    }),
    // 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
    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: ,
    // 检测文件是否发生变化的间隔时长,默认每隔1000毫秒询问一次
    poll: 
  },
  
  // 输出文件性能检查配置
  performance: { 
    hints: 'warning', // 有性能问题时输出警告
    hints: 'error', // 有性能问题时输出错误
    hints: false, // 关闭性能检查
    maxAssetSize: , // 最大文件大小 (单位 bytes)
    maxEntrypointSize: , // 最大入口文件大小 (单位 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文件中的资源的引入路径
*/

webpack工程搭建的更多相关文章

  1. 部署React+webpack工程的步骤

    # 部署React+webpack工程的步骤ps:以Mac os系统做开发环境.因为npm现在使用灰常的慢,所以我使用淘宝境像cnpm. 1,准备工作: 先确保存已经安装了node.js: 2,文件部 ...

  2. 某马-某淘商城的day01--->分析,工程搭建,tomcat插件启动工程,svn,反思

    -1:为什么还要写某马的某淘商城呢? 答:万物更新,季节交替.新人总把旧人换.所以呢,前人写了N多遍的东西,我们依旧在学,所以下决心写某马的某淘商城.(也因为在学校还没找工作,所以找个相对长期的事情做 ...

  3. Django---框架简介和工程搭建

    Django框架 一.Django介绍 二.Django工程搭建 回到顶部 一.Django介绍 1.简介    Django的主要目的是简便.快速的开发数据库驱动的网站.它强调代码复用,多个组件可以 ...

  4. 单工程搭建springmvc+spring+mybatis(maven,idea)

    单工程搭建springmvc+spring+mybatis(maven,idea) 1.pom.xml <properties> <project.build.sourceEncod ...

  5. Mybatis工程搭建

    工程搭建 • 1依赖包 • 2配置文件 • 2.1spring-mybatis.xml • 2.2mybatis-config.xml自带配置文件 • 2.3 mapper(dao)对象 • 2.4 ...

  6. 使用Yarn+Webpack+Babel6搭建React.js环境

    使用Yarn+Webpack+Babel6搭建React.js环境 Facebook开源的React.js已经改变了世人对前端UI的思考方式.这种基于组件方式的优势之一,就是使View更加的简单,因为 ...

  7. JAVAEE——宜立方商城01:电商行业的背景、商城系统架构、后台工程搭建、SSM框架整合

    1. 学习计划 第一天: 1.电商行业的背景. 2.宜立方商城的系统架构 a) 功能介绍 b) 架构讲解 3.工程搭建-后台工程 a) 使用maven搭建工程 b) 使用maven的tomcat插件启 ...

  8. JAVAEE——淘淘商城第一天:电商行业的背景和技术特点,商城的介绍、技术的选型、系统架构和工程搭建

    1. 学习计划 1.电商行业的背景. 2.电商行业的技术特点 3.商城的介绍 a) 常用的名词介绍 b) 系统功能介绍 4.淘淘商城的系统架构 a) 传统架构 b) 分布式架构 c) 基于服务的架构 ...

  9. SpringBoot - 工程搭建

    SpringBoot 简介 1.Spring 的封装.其设计目的是用来简化 Spring 应用的初始搭建以及开发过程. 2.SpringCloud 微服务的基础 搭建环境 jdk 1.8 + mave ...

随机推荐

  1. MVC Action 返回类型

    https://www.cnblogs.com/xielong/p/5940535.html https://blog.csdn.net/WuLex/article/details/79008515 ...

  2. 我的es6笔记

    变量 1. let 和 const 声明的变量不在window上了 2. es6中对于块级作用域里的函数声明实现不统一,要避免在大括号里声明函数,尽量用函数表达式来替代. 3. let和const声明 ...

  3. WCF服务could not be activated

    The requested service, 'http://10.10.10.143/XmlEditorService/XmlEditorService.svc' could not be acti ...

  4. 面试题_lambda函数调用

    res多少? def func(): return [lambda x: i * x for i in range(4)] res = [m(2) for m in func()] # print(r ...

  5. 删除N天前的文件(夹)与拷贝文件到共享盘的批处理

    rem ======= 删除目录 7天前目录 ========= forfiles /P "E:\Folder" /S /D -7 /C "Cmd /C If @isdi ...

  6. 第三方deepvoice3_pytorch安装使用

    项目地址:https://github.com/r9y9/deepvoice3_pytorch 解决两个问题:一使用外部数据集训练,二加载pre-trained模型做fine-tune 1:安装必要依 ...

  7. 检测Tensorflow可用设备(比如:显卡)

    打开python命令行,输入以下命令: python -c "from tensorflow.python.client import device_lib;device_lib.list_ ...

  8. 闪动效果的实现 (jquery方式和css方式)以及 keyframes和opacity 与ie等各浏览器兼容问题

    opacity 是CSS3中:设置元素的不透明度的属性(支持ie8以上) opacity: value|inherit;value用于规定不透明度.从 0.0 (完全透明)到 1.0(完全不透明). ...

  9. springboot学习四:整合mybatis

    在application.properties加入配置 ## Mybatis 配置 mybatis.typeAliasesPackage=org.spring.springboot.domain my ...

  10. C#C/S框架演示 (MES系统)

    之前做过一个MES系统,发一些里面的截图.如果有朋友也用这个框架.或者有兴趣可以一起学习学习.使用开发工具VS2013,数据库SqlServer2008和Oracle11C.插件dev15.2,开发模 ...