如何使用一些技术?

251 阅读11分钟

h5 storage事件 监听;

“当同源页面的某个页面修改了localStorage,其余的同源页面只要注册了storage事件,就会触发”

localStorage的例子运行需要如下条件

  1. 同一浏览器打开了两个同源页面
  2. 其中一个网页修改了localStorage
  3. 另一网页注册了storage事件
  4. 很容易犯的错误是,在同一个网页修改本地存储,又在同一个网页监听,这样是没有效果的
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快的一个原因

  1. 参考地址:www.cnblogs.com/2050/p/4198…
  2. gulp.src(匹配文件路径,option)获取stream流的;
  3. 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
  1. 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 为文件变化后要执行的任务。
  1. 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);

blog.csdn.net/weixin_4242…

  1. String.prototype.charCodeAt()

    返回指定索引处字符的 Unicode 数值(Unicode 编码单元 > 0x10000 的除外)。 Unicode 编码单元(code points)的范围从 0 到 1,114,111。开头的 128 个 Unicode 编码单元和 ASCII 字符编码一样。 如果指定的 index 小于 0 或大于字符串的长度,则 charCodeAt 返回 NaN。

  2. 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!'))

  1. 读取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()
    }
  }
}
  1. 中间处理文件
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;

  1. 遍历选择本地数据

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文件。

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. 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区别

  1. 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务;
  2. plugins:插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

manifest

在使用 webpack 构建的典型应用程序或站点中,有三种主要的代码类型:

你或你的团队编写的源码。
你的源码会依赖的任何第三方的 library"vendor" 代码。
webpack 的 runtime 和 manifest,管理所有模块的交互。

wepback module 配置

  1. 条件匹配:通过test、include、exclude来应用规则的文件;
  2. 对选中后的文件通过 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'],
    },
  ]
}
  1. optimization.splitChunks.cacheGroups 将公共文件分包处理;

    multiple 打包替换问题?????

学习到打包到getWebpackConfig

  1. 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 用于并行执行