h5 storage事件 监听;
“当同源页面的某个页面修改了localStorage,其余的同源页面只要注册了storage事件,就会触发”
localStorage的例子运行需要如下条件
- 同一浏览器打开了两个同源页面
- 其中一个网页修改了localStorage
- 另一网页注册了storage事件
- 很容易犯的错误是,在同一个网页修改本地存储,又在同一个网页监听,这样是没有效果的
class Storage {
private typeName: string;
constructor (props:Iprops) {
this.init(props)
this.typeName = props.typeName
}
public init (props:Iprops) {
this.bind()
this.trigger(props.typeName)
}
// 首次强行启动监听修改缓存,第一步
public trigger (typeName:string) {
if(!sessionStorage.getItem(typeName)){
localStorage.setItem("getSession", String(Date.now()));
}
return
}
public bind () {
window.addEventListener("storage", this.listener)
}
public listener = (event:any) => {
window.console.log(event);
if (!event.newValue) { return ;}
if(event.key === "getSession"){
// 将第二步 , 另外一个页面, 将用户的用户信息存入到localstorage中,然后出发了key= storeSessionData ; 将用户信息流向到event.newValue中
localStorage.setItem("storeSessionData", sessionStorage.getItem(this.typeName) || '');
localStorage.removeItem("storeSessionData");
}
if(event.key === "storeSessionData"){
// 第三步,回到这个页面 将用户信息设置到session中
sessionStorage.setItem(this.typeName, event.newValue);
localStorage.removeItem("getSession");
}
if(event.key === `updateSession_${this.typeName}`){
sessionStorage.setItem(this.typeName, event.newValue);
localStorage.removeItem("updateSession");
}
}
public update (data:any, name:string = 'boms') {
localStorage.setItem(`updateSession_${name}UserInfo`, JSON.stringify(data));
}
public unbind () {
window.removeEventListener('storage', this.listener)
}
}
gulp 命令行执行:gulp
使用gulp,仅需知道4个API即可:gulp.task(),gulp.src(),gulp.dest(),gulp.watch()
在Gulp中,使用的是Nodejs中的stream(流),首先获取到需要的stream,然后可以通过stream的pipe()方法把流导入到你想要的地方,比如Gulp的插件中,经过插件处理后的流又可以继续导入到其他插件中,当然也可以把流写入到文件中。所以Gulp是以stream为媒介的,它不需要频繁的生成临时文件,这也是Gulp的速度比Grunt快的一个原因
- 参考地址:www.cnblogs.com/2050/p/4198…
- gulp.src(匹配文件路径,option)获取stream流的;
- gulp.dest(生成路径)方法是用来写文件的,就要理解给它传入的路径参数与最终生成的文件的关系。
- 给gulp.dest()传入的路径参数,只能用来指定要生成的文件的目录,而不能指定生成文件的文件名,它生成文件的文件名使用的是导入到它的文件流自身的文件名,所以生成的文件名是由导入到它的文件流决定的,即使我们给它传入一个带有文件名的路径参数,然后它也会把这个文件名当做是目录名
var gulp = require('gulp');
gulp.src('script/jquery.js')
.pipe(gulp.dest('dist/foo.js'));
//最终生成的文件路径为 dist/foo.js/jquery.js,而不是dist/foo.js
- gulp.task方法用来定义任务,主要是要知道当依赖是异步任务时的处理。
gulp.task(name[, deps], fn)
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { //定义一个有依赖的任务
// Do something
});
* name 为任务名
* deps 是当前定义的任务需要**依赖的其他任务**,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数
* fn 为任务函数,我们把任务要执行的代码都写在里面。该参数也是可选的。
如果任务相互之间没有依赖,任务会按你书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。
但是如果某个任务所依赖的任务是异步的,就要注意了,gulp并不会等待那个所依赖的异步任务完成,而是会接着执行后续的任务。
5. gulp.watch() 用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务
- gulp.watch(glob[, opts], tasks)
- glob 为要监视的文件匹配模式,规则和用法与gulp.src()方法中的glob相同。
- opts 为一个可选的配置对象,通常不需要用到。
- tasks 为文件变化后要执行的任务。
- gulp4在gulp3的基础上新增了几个函数,但它的使用依旧简单方便。 - **gulp.parallel() –并行运行任务 ** - gulp.series() –运行任务序列 - gulp.symlink() –与dest相似,但是使用软连接形式 - gulp.lastRun() –获得上次成功运行的时间戳 - gulp.tree() –获得任务书树 - gulp.registry() –获得或注册任务
gulp.task('default',gulp.parallel('taskA','taskB'));//并行执行
gulp.task('default',gulp.series('taskA','taskB'));//按顺序执行
一、 jsencrypt 非对称加密
1. 获取公钥;
2. 利用jsencrypt,设置公钥key,
3. 对密码进行加密;
// 从后台获取公钥(data),这里省略,直接赋值
let publicKey = '-----BEGIN PUBLIC KEY-----' + data + '-----END PUBLIC KEY-----'
let encryptor = new JSEncrypt() // 新建JSEncrypt对象
encryptor.setPublicKey(publicKey) // 设置公钥
let rsaPassWord = encryptor.encrypt(password) // 对密码进行加密
Demo
fezsApp.secretKey = function(callback) {
import('jsencrypt').then(module => {
const JSEncrypt = module.JSEncrypt;
let publicKey;
//公钥加密
fezsApp.encryptData = function(password) {
var encrypt = new JSEncrypt();
fezsApp.getPublicKey(function(data) {
publicKey = '-----BEGIN PUBLIC KEY-----' + data + '-----END PUBLIC KEY-----';
});
encrypt.setPublicKey(publicKey);
var encrypted = encrypt.encrypt(password);
return encrypted;
};
callback && callback();
});
};
二、 base64加密
base64是网络上最常见的用于传输8bit字节代码的编码方式之一。有时我们需要把二进制数据编码为适合放在URL中的形式。这时采用base64编码具有不可读性,即所编码的数据不会被人直接看出。除此之外,还可以放在请求头,响应头进行传输。
// base64 加密
function Base64() {
// private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
//1.加密
var str = '124中文内容';
var base = new Base64();
var result = base.encode(str);
//2.解密
var result2 = base.decode(result);
-
String.prototype.charCodeAt()
返回指定索引处字符的 Unicode 数值(Unicode 编码单元 > 0x10000 的除外)。 Unicode 编码单元(code points)的范围从 0 到 1,114,111。开头的 128 个 Unicode 编码单元和 ASCII 字符编码一样。 如果指定的 index 小于 0 或大于字符串的长度,则 charCodeAt 返回 NaN。
-
String.fromCharCode() String.fromCharCode() 静态方法根据指定的 Unicode 编码中的序号值来返回一个字符串。
一、计算
/**
* 避免小数计算误差的,两个数相加
*/
function add(arg1: number, arg2: number): number {
// 加
let r1;
let r2;
let m;
let c;
try {
r1 = arg1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
c = Math.abs(r1 - r2);
m = Math.pow(10, Math.max(r1, r2));
if (c > 0) {
let cm = Math.pow(10, c);
if (r1 > r2) {
arg1 = Number(arg1.toString().replace('.', ''));
arg2 = Number(arg2.toString().replace('.', '')) * cm;
} else {
arg1 = Number(arg1.toString().replace('.', '')) * cm;
arg2 = Number(arg2.toString().replace('.', ''));
}
} else {
arg1 = Number(arg1.toString().replace('.', ''));
arg2 = Number(arg2.toString().replace('.', ''));
}
return (arg1 + arg2) / m;
}
// 减
function sub(arg1: number, arg2: number): number {
let r1;
let r2;
let m;
let n;
try {
r1 = arg1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
n = r1 >= r2 ? r1 : r2;
return Number(((arg1 * m - arg2 * m) / m).toFixed(n));
}
// 乘
function mul(arg1: number | string, arg2: number): number {
if (arg1 == undefined || arg2 == undefined) {
throw TypeError('calculate mul args should not be undefined');
}
let m = 0;
let s1 = arg1.toString();
let s2 = arg2.toString();
try {
m += s1.split('.')[1].length;
} catch (e) {}
try {
m += s2.split('.')[1].length;
} catch (e) {}
return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m);
}
function div(arg1: number, arg2: number): number {
// 除
let t1 = 0;
let t2 = 0;
let r1;
let r2;
try {
t1 = arg1.toString().split('.')[1].length;
} catch (e) {}
try {
t2 = arg2.toString().split('.')[1].length;
} catch (e) {}
r1 = Number(arg1.toString().replace('.', ''));
r2 = Number(arg2.toString().replace('.', ''));
return (r1 / r2) * Math.pow(10, t2 - t1);
}
/**
* 保留几位小数的四舍五入
*/
function round(value: number | string, num: number = 2): number {
let n = Math.round(mul(value, Math.pow(10, num)));
let val = div(n, Math.pow(10, num));
return val;
}
export default { add, sub, mul, div, round };
二、兼容Object.keys()
const keys = (function() {
const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasDontEnumBug = !{ toString: null }.propertyIsEnumerable('toString');
const dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor',
];
const dontEnumsLength = dontEnums.length;
if (Object.keys) {
return Object.keys;
}
return function(obj: any) {
if ((typeof obj !== 'object' && typeof obj !== 'function') || obj === null)
throw new TypeError('Object.keys called on non-object');
var result = [];
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) result.push(prop);
}
if (hasDontEnumBug) {
for (var i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
}
}
return result;
};
})();
export default keys;
三、获取对象深层次的 属性
import isArray from './isArray';
function getIn(obj: any, keys?: any[] | string, def?: any) {
let ret = typeof obj === 'undefined' ? def : obj;
let len;
let i = 0;
if (!obj || !keys) {
return ret;
}
if (typeof keys === 'string') {
return typeof obj[keys] !== 'undefined' ? obj[keys] : def;
} else if (isArray(keys)) {
len = keys.length;
while (i < len) {
ret = ret[keys[i]];
// 不是最后最后一项
if (i !== len - 1 && !ret) {
return def;
}
i++;
}
}
return typeof ret === 'undefined' ? def : ret;
}
export default getIn;
四、原生样式处理
/**
* 删除 Dom 元素的某项属性
*
* @export
* @param {*} element
* @param {*} attributeName
* @returns
*/
export default function (element, attributeName, value) {
if (!element || !attributeName) {
return element;
}
return element.setAttribute(attributeName, value)
}
1. 启动服务
const express = require('express')
const apiJsonMiddleware = require('./apiJsonMiddleware')
const app = express()
const apiJsonMiddlewareDeration = apiJsonMiddleware({name:'group'})
app.all('*',function(req,res){
apiJsonMiddlewareDeration(req,res)
// res.send("fdsafsdaf")
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))
- 读取Josn
const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')
const path = require('path');
let fileApiMiddleware = require('./fileApiMiddleware')
module.exports = function(options) {
const fileApi = fileApiMiddleware(options)
// eslint-disable-next-line no-console
return function (req, res) {
const name = options.name || 'customer';
const adapter = new FileSync(path.join(__dirname,`./data/${name}.json`))
const db = low(adapter)
let returnData = null;
let curPath = req.url.replace(/^\/\//, '');
let apiName = curPath.split('?')[0].replace(/^\//, ''); // 去除查询
returnData = db.get(apiName).value()
// 404
if (!returnData) {
returnData = {
success: false,
code: 404,
msg: `path = ${curPath} 没找到`,
}
fileApi(req, res)
} else {
res.write(JSON.stringify(returnData))
res.send()
}
}
}
- 中间处理文件
const fs = require('fs');
const path = require('path');
const mime = require('mime');
const changeCase = require('change-case');
const JSON5 = require('json5');
const getLength = require('utf8-byte-length')
const travelDirRecursive= require('./travelDirRecursive')
/**
* 把路径拼装成驼峰式 文件名 transform
*
* @param {*} url
* @returns
*/
function transformPath(url) {
let ret = url.split('?')[0];
let newArr = [];
const pathArr = ret.split('/');
// 过来空,拼接成驼峰式命名
newArr = pathArr.filter((item) => item).map((item, i) => {
let pathRet = item;
if (i > 0) {
pathRet = changeCase.upperCaseFirst(item);
}
return pathRet;
});
ret = newArr.join('').replace(/.action$/, '');
return path.normalize(`${ret}.json`);
}
/**
* 简单的 http api connect 中间件请求处理,
* 把特定路径请求的按路径访问相应的json文件
*
* @param {[object]} options 初始化选项
* @return {[function]} 处理函数
*/
function serverApi(options) {
const name = options.name || 'customer';
return (req, res) => {
// handle any requests at /api
const rootUrl = path.join(__dirname, `./data/${name}`);
const commonUrl = path.join(__dirname, './data/common');
const reqFilename = transformPath(req.url);
const fileMime = mime.getType(reqFilename);
let isDone = false;
function readDone(resText, url) {
let msg = resText;
let delay = 10;
if (isDone) {
return null;
}
if (!resText) {
msg = JSON.stringify({
success: false,
code: 404,
msg: `path = ${url} 没找到`,
});
} else {
let newObject
try {
newObject = JSON5.parse(msg);
delay = newObject.delay;
msg = JSON.stringify(newObject);
} catch (error) {
newObject = msg
}
}
isDone = true;
setTimeout(() => {
// res.setHeader("Content-Range", "bytes");
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', `${fileMime}; charset=UTF-8`);
res.setHeader('Content-Length', getLength(msg));
res.end(msg);
}, delay)
return null;
}
const travelCallback = (fileInfo, next) => {
if (fileInfo.filename === reqFilename) {
const resText = fs.readFileSync(fileInfo.fullPath);
readDone(resText, fileInfo.fullPath);
} else {
next()
}
// 遍历完成,还没有找到
}
// 递归遍历 common 文件夹,找到文件停止递归
return travelDirRecursive(commonUrl, travelCallback)
// 在产品文件夹未找到
.then(() => {
// 递归遍历具体产品文件夹,找到文件停止递归
return travelDirRecursive(rootUrl, travelCallback)
})
// 遍历完成,还没有找到
.then(() => {
readDone('', reqFilename)
}).catch( () => {
readDone('', reqFilename)
});
};
}
module.exports = serverApi;
- 遍历选择本地数据
const fs = require('fs');
const path = require('path');
/**
* 异步递归遍历文件夹下的文件
*
* @param {string} dir 文件夹路径
* @param {func} callback 每个文件的回调处理函数
* @returns Promise 参数 为文件路径列表
*/
const travelDirRecursive = (rootDir, callback) => {
var fileList = [];
var remainTimes = 1;
return new Promise((resolve, reject) => {
function travel(dir, callback, finish) {
fs.readdir(dir, function(err, files) {
console.log(err,'err')
console.log(files,'files')
var len = files.length;
remainTimes -= 1;
if (err) reject(err);
remainTimes += len;
(function next(i) {
if (i < len) {
var pathname = path.join(dir, files[i]);
fs.stat(pathname, (err, stats) => {
if (err) reject(err);
if (stats.isDirectory()) {
travel(pathname, callback, () => {
next(i + 1);
});
} else {
fileList.push(pathname);
// 每个文件的回调函数
if (typeof callback === 'function') {
callback(
{
fullPath: pathname,
path: path.relative(rootDir, pathname),
filename: files[i],
dir: dir,
},
() => {
remainTimes -= 1;
next(i + 1);
},
);
}
}
});
// 超出文件
} else {
finish && finish();
if (remainTimes <= 0) {
resolve(fileList);
}
}
})(0);
});
}
travel(rootDir, callback);
});
}
module.exports = travelDirRecursive;
webpack 四个重要的核心概念
入口(entry)
1.配置多页面入口,{多键值对}
输出(entry)
loader
loader让webpack能够去处理那些非JavaScript文件。
- test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
- use 属性,表示进行转换时,应该使用哪个 loader。
{
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};
解释为: "嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先使用 raw-loader 转换一下。"
插件(plugins)
loader 与 plugins区别
- 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务;
- plugins:插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
manifest
在使用 webpack 构建的典型应用程序或站点中,有三种主要的代码类型:
你或你的团队编写的源码。
你的源码会依赖的任何第三方的 library 或 "vendor" 代码。
webpack 的 runtime 和 manifest,管理所有模块的交互。
wepback module 配置
- 条件匹配:通过test、include、exclude来应用规则的文件;
- 对选中后的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。
module: {
rules: [
{
// 命中 JavaScript 文件
test: /\.js$/,
// 用 babel-loader 转换 JavaScript 文件
// ?cacheDirectory 表示传给 babel-loader 的参数,用于缓存 babel 编译结果加快重新编译速度
use: ['babel-loader?cacheDirectory'],
// 只命中src目录里的js文件,加快 Webpack 搜索速度
include: path.resolve(__dirname, 'src')
},
{
// 命中 SCSS 文件
test: /\.scss$/,
// 使用一组 Loader 去处理 SCSS 文件。
// 处理顺序为从后到前,即先交给 sass-loader 处理,再把结果交给 css-loader 最后再给 style-loader。
use: ['style-loader', 'css-loader', 'sass-loader'],
// 排除 node_modules 目录下的文件
exclude: path.resolve(__dirname, 'node_modules'),
},
{
// 对非文本文件采用 file-loader 加载
test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
use: ['file-loader'],
},
]
}
-
optimization.splitChunks.cacheGroups 将公共文件分包处理;
multiple 打包替换问题?????
学习到打包到getWebpackConfig
-
apply 中 ompiler.hooks.emit.tap('appThemePlugin', compilation=>{}) 回调中compilation 包含所有的资源 Object.keys(compilation.assets).forEach(asset => { // 中间有 asset ,对应的文件链接 let source = compilation.assets[asset].source(); // source 对应文件里所有的字符串资源; if (asset.indexOf('.css') > -1 && !this.hasExcudesPath(asset)) { let css = this.filterCSSMainColor(source); if (css) { themeCSS.push(css); } }
if (asset.indexOf('.html') > -1 && !this.hasExcudesPath(asset)) { compilation.assets[asset] = this.insertThemeJStoHead(source); }});
http-proxy-middleware用于后台将请求转发给其它服务器。
lowdw 读取本地JSON
gulp 和 webpack 的区别
Gulp 的定位是 Task Runner, 就是用来跑一个一个任务的。
放在以前比如我想用sass写css, coffee写js, 我必须手动的用相应的compiler去编译各自的文件,然后各自minify。这时候designer给你了两张新图片,好嘞,接着用自己的小工具手动去压缩图片。
后来前端人不能忍了,搞出个自动化这个流程的 Grunt/Gulp, 比如你写完代码后要想发布production版本,用一句 gulp build 就可以rm 掉 dist文件夹中以前的旧文件,自动把sass编译成css, coffee编译成js压缩各自的文件,压缩图片,生成图片sprite
拷贝minified/uglified 文件到 dist 文件夹但是它没发解决的是 js module 的问题,是你写代码时候如何组织代码结构的问题.
之前大家可以用 require.js, sea.js 来 require dependency, 后来出了一个 webpack 说 我们能不能把所有的文件(css, image, js) 都用 js 来 生成依赖,最后生成一个bundle呢? 所以webpack 也叫做file bundler.
同时 webpack 为了解决可以 require 不同文件的需求引入了loader, 比如面对sass文件有
sass-loader, 把sass 转换成 css
css-loader, 让 webpack 能识别处理 css
style-loader, 把识别后的 css 插入到 html style中
类似的识别es6 有babel-loader
本来这就是 webpack 的初衷,require everything, bundle everything. 一开始 webpack 刚出来的时候大家都是把它结合着 gulp 一起用的, gulp 里面有个 gulp-webpack,就是让 webpack 专门去做module dependency的事情, 生成一个bundle.js文件,然后再用 gulp 去做一些其他杂七杂八minify, uglify的事情。 后来人们发现 webpack 有个plugins的选项, 可以用来进一步处理经过loader 生成的bundle.js,于是有人写了对应的插件, 所以minify/uglify, 生成hash的工作也可以转移到webpack本身了,挤掉了gulp这部分的市场份额。 再后来大家有发现 npm/package.json 里面的scripts 原来好好用啊,调用任务的时候就直接写一个简单的命令,因为 gulp 也不就是各种插件命令的组合呀,大部分情况下越来越不需要 gulp/grunt 之类的了 ref. 所以你现在看到的很多新项目都是package.json里面scripts 写了一堆,外部只需要一个webpack就够了。
打个不恰当的比方,webpack就像微信一样,本来就是做聊天(module dependency)的,后来生生搞出一个微信小程序(processing files),大家面对简单的需求发现这个比原生app方便使用啊,于是开发原生的人越来越少一样。
所以 LZ 一开始就模仿其他项目用 npm scripts + webpack 就好了,当你发现有哪些任务你没法用 webpack 或者npm scripts 解决起来麻烦, 这个时候再引入task runner 也不迟
gulp Api
gulp.series 用于串行(顺序)执行 ulp.parallel 用于并行执行