1、webpack.devServer配置
如果要启用webpack代理,则配置如下:
webpackConfig.devServer = {
hot: true,
hotOnly: false, // If true: Enable HMR without page refresh when build failure
open: true, // auto open
port: 8080,
overlay: true,
publicPath: '/',
proxy: {
'(/crmp/admin/**)': {
target : 'http://10.6.183.146:8088/admin',
changeOrigin: true,
secure: false, // 接受运行在 HTTPS 上,且使用了无效证书的后端服务器
},
},
quiet: true,
}
proxy中的代理请求地址必须是相对路径,且从相对路径左侧开始匹配,如 (/crmp/admin/xxx)代理的是 http://localhost:8080/crmp/admin/xxx请求路径,代理到后端http://10.6.183.146:8088/admin/xxx地址。
代理后,我们在浏览器网络栏看到的请求地址是:
Request URL:http://localhost:8080/admin/xxx。
实际是代理服务器请求了后端接口,再把数据响应给localhost:8080。
2、搭建基于webpack4+vue2.6搭建swig模板渲染多页应用骨架
(1)项目结构
(2)webpack.config.js配置文件:
var path = require('path')
var utils = require('./utils')
const webpack = require('webpack')
const ENV_CONFIG = require('./config')
const vueLoaderConfig = require('./vue-loader.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const AddAssetPlugin = require('add-asset-html-webpack-plugin')
const env = process.env.NODE_ENV
const isDev = env === 'dev'
var entries = utils.getEntry('./src/views/**/main.js') // 获得入口 js 文件
var pages = utils.getHtmlConfig(false) // 获得多页面 HTML 模板文件
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
var webpackConfig = {
mode: isDev ? 'development' : 'production',
entry: entries,
output: {
path: ENV_CONFIG[env].assetsRoot,
publicPath: ENV_CONFIG[env].assetsPublicPath,
filename: isDev ? '[name].js' : 'js/[name].js?v=[contenthash:7]',
chunkFilename: isDev ? '[name].chunk.js' : 'js/[name].chunk.js?v=[contenthash:7]'
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'src': resolve('src'),
'assets': resolve('src/assets'),
'utils': resolve('src/utils'),
'api': resolve('src/api'),
'views': resolve('src/views'),
'plugins': resolve('src/plugins'),
'components': resolve('src/components')
}
},
resolveLoader: {
modules: [resolve('node_modules'), resolve('loaders')]
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
exclude: /node_modules/,
include: resolve('src'),
exclude: resolve('src/assets/js/swiper.min.js'),
options: {
presets: ['@babel/preset-env']
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig,
include: [resolve('src/views'), resolve('src/components'), resolve('node_modules/@pa/widget-loader/src/')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 1000,
publicPath: ENV_CONFIG[env].assetsPublicPath,
name: 'images/[name].[ext]?v=[contenthash:7]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 1000,
publicPath: ENV_CONFIG[env].assetsPublicPath,
name: 'fonts/[name].[ext]?v=[contenthash:7]'
}
}
].concat(
utils.styleLoaders({ sourceMap: ENV_CONFIG[env].cssSourceMap, extract: !isDev })
)
},
devtool: isDev ? 'cheap-module-eval-source-map' : 'cheap-module-source-map',
plugins: [
...pages.map(page => new HtmlWebpackPlugin(page)),
new VueLoaderPlugin(),
new AddAssetPlugin({
filepath: resolve('dll/vendor.dll.js')
}),
new webpack.DllReferencePlugin({
manifest: resolve('dll/vendor.manifest.json')
}),
// new AddAssetPlugin({
// filepath: resolve('dll/patool.dll.js')
// }),
// new webpack.DllReferencePlugin({
// manifest: resolve('dll/patool.manifest.json')
// }),
new webpack.DefinePlugin({
NODE_ENV: JSON.stringify(env),
PUBLIC_PATH: JSON.stringify(ENV_CONFIG[env].assetsPublicPath),
'process.env':JSON.stringify({
MOCK_ENV: isDev && ENV_CONFIG.dev.enableRapMock && 'MOCK',
})
}),
new CopyPlugin([
{ from: resolve('src/assets/static'), to: 'static', ignore: ['.*'] },
{ from: resolve('src/assets/static'), to: 'static', ignore: ['.*'] }
])
],
optimization: {
minimize: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
name: 'vendors', //若默认名称将根据入口实际共用生成多个组,按需引入;设置名称只生成一个公共组,全部页面引入
test: /[\\/]node_modules[\\/]/,
priority: -10,
minChunks: 1 //wait 3?
},
default: { //显示配置default分组才能覆盖默认default分组(minChunks: 2)
name: 'common', // 若默认名称将根据入口实际共用生成多个组,根据实际引入;设置名称只生成一个公共组,全部页面引入
minSize: 100, //大小超过100个字节
minChunks: 3, //最少引入了3次
priority: -20,
reuseExistingChunk: true
}
}
},
runtimeChunk: false
}
}
if (isDev) {
webpackConfig.devServer = {
hot: true,
hotOnly: false, // If true: Enable HMR without page refresh when build failure
open: ENV_CONFIG.dev.open,
port: ENV_CONFIG.dev.port,
overlay: true,
publicPath: ENV_CONFIG.dev.assetsPublicPath,
proxy: ENV_CONFIG.dev.proxyTable,
quiet: true,
}
} else {
webpackConfig.plugins.push(new MiniCssExtractPlugin({
filename: 'css/[name].css?v=[contenthash:7]',
chunkFilename: 'css/[id].css?v=[contenthash:7]'
}))
webpackConfig.plugins.push(new OptimizeCssAssetsPlugin())
}
module.exports = webpackConfig
(3)webpack.dll.config.js配置文件:
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry: {
vendor: [
'vue/dist/vue.esm.js',
'babel-polyfill'
],
// patool: [
// '@pa/poppy-ui'
// ]
},
output: {
path: path.join(__dirname, '../dll'),
filename: '[name].dll.js',
library: '[name]_library'
},
devtool: 'cheap-module-source-map',
plugins: [
new webpack.DllPlugin({
name: '[name]_library',
path: path.join(__dirname, '../dll', '[name].manifest.json')
})
]
}
(4)src/plugins/index.js文件:
import mixins from './mixins'
import * as filters from './filters'
import * as directives from './directives'
let COMPOSE_PLUGIN = {}
COMPOSE_PLUGIN.install = function(Vue, options) {
// global directives
Object.keys(directives).forEach(key => {
Vue.directive(key, directives[key])
})
// global filters
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
// global mixins
Vue.mixin(mixins)
}
export default COMPOSE_PLUGIN
(4)统一请求封装Service.js文件:
function Service() {
this.request = function(opts) {
this.opts = Object.assign({}, Service.DEFAULTS, opts)
if (window.dingding && dingding.http && dingding.http.request) {
return this._dingdingRequest()
} else if (window.$ && $.ajax) {
return this._ajaxRequest()
} else { // 原生请求
return this._xhrRequest()
}
}
this.get = function(opts) {
return this.request((opts.method = 'GET') && opts)
}
this.post = function(opts) {
return this.request((opts.method = 'POST') && opts)
}
}
Service.DEFAULTS = {
url: '',
method: 'GET',
contentType: 'application/x-www-form-urlencoded;charset=utf-8',
dataType: 'json', // 期待返回数据类型
timeout: 15, // 请求超时设置
data: {}
}
// dingding request
Service.prototype._dingdingRequest = function () {
console.log('请求工具-->', 'dingding');
const config = {
url: this.opts.url,
method: this.opts.method,
headers: {
'Content-Type': this.opts.contentType
},
timeout: this.opts.timeout,
xhrFields: {
withCredentials: true
}
}
if (config.method.toUpperCase() === 'POST') {
config.body = this.opts.data
} else {
config.qs = this.opts.data
}
return new Promise(function(resolve, reject) {
dingding.http.request(config, function (error, res) {
if (error) { // 调用失败
// dingding.toast.show({
// message: '系统异常,请稍后再试!'
// })
reject('AJAX_ERROR')
return
}
if (res.status === 200) {
isJsonStr(res.body) ? resolve(JSON.parse(res.body)) : resolve(res.body) // res.body 响应原始内容
} else {
reject(res.status + res.statusText) // 响应状态码 + 状态码对应的文本(如 200 -- OK)
}
})
})
}
// ajax request
Service.prototype._ajaxRequest = function () {
console.log('请求工具-->', '$.ajax');
let config = {
url: this.opts.url,
type: this.opts.method,
data: this.opts.data, // get请求时,会自动序列化参数后添加到url末尾
headers: {
'Content-Type': this.opts.contentType
},
dataType: this.opts.dataType,
timeout: this.opts.timeout,
xhrFields: {
withCredentials: true
}
}
return new Promise(function(resolve, reject) {
// when request succeeds
config.success = function(data, status, xhr) {
if (xhr.status === 200) {
resolve(data) // data: 响应原始内容
} else {
reject(xhr.status + xhr.statusText) // 响应状态码 + 状态码对应的文本(如 200 -- OK)
}
}
// if there is an error (timeout, parse error, or status code not in HTTP 2xx)
config.error = function(xhr, textStatus) {
reject('AJAX_ERROR')
}
$.ajax(config)
})
}
// XMLHttpRequest request
Service.prototype._xhrRequest = function () {
console.log('请求工具-->', 'XMLHttpRequest');
let url = this.opts.url
const method = this.opts.method.toUpperCase()
const data = this.opts.data
// 如果是 GET 请求,需要处理 data 里的数据并且以一定的规则拼接到 url 后
if (method === 'GET') {
var formData = []
for(key in this.opts.data) {
formData.push(''.concat(key, '=', this.opts.data[key]))
}
let params = formData.join('&')
if (params.length > 0) {
url += url.indexOf('?') >= 0 ? ''.concat('&', params) : ''.concat('?', params)
}
}
let xhr = null
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest()
} else if (window.ActiveXObject) {
xhr=new ActiveXObject("Microsoft.XMLHTTP")
}
xhr.timeout = this.opts.timeout
// xhr.responseType = 'json' // 指定类型与服务器返回值类型若是兼容的,则按指定类型返回,若不兼容则返回null
xhr.withCredentials = true
xhr.open(method, url, true)
xhr.setRequestHeader('Content-Type', this.opts.contentType)
return new Promise(function(resolve, reject) {
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
isJsonStr(xhr.response) ? resolve(JSON.parse(xhr.response)) : resolve(xhr.response) // xhr.response,整个响应体
} else {
reject(xhr.status + xhr.statusTxt) // 响应状态码 + 状态码对应的文本(如 200 -- OK)
}
}
}
xhr.onerror = function(e) {
reject('AJAX_ERROR')
}
xhr.ontimeout = function(e) { }
xhr.send(method === 'POST' ? data : null)
})
}
// 检查是否JSON文本
const isJsonStr = function(value){
try {
eval('('+value+')')
return true
} catch(er){
return false
}
}
// module.exports = {
// service: new Service()
// }
var req = new Service()
// var p = req.request({
// url: '/hget',
// method: 'GET',
// data: { name: 'zhangsan' }
// })
var p = req.request({
url: '/hput',
method: 'PUT',
data: { name: 'zhangsan' }
})
// var p = req.request({
// url: '/hpost',
// method: 'POST',
// data: { name: 'zhangsan' }
// })
p.then(res => console.log('resolved', res)).catch(err => console.log('rejected', err))
1、babel转译和babel-polyfill垫片
这里需要理解下三个概念:
- 最新ES 语法:比如,箭头函数
- 最新ES API:,比如,Promise
- 最新ES 实例方法:比如,String.protorype.includes
@babel/preset-env
默认支持语法转化,需要开启useBuiltIns
配置才能转化API和实例方法。
此外,不管是写项目还是写Library/Module,使用@babel/preset-env
并正确配置就行。多看英文原稿说明,中文总结看看就好,别太当真。
参考一:关于Babel你只需要知道三个插件
参考二:babel-preset-env使用指南
参考三:useBuiltIns
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。