zoukankan      html  css  js  c++  java
  • Node:通过 Uglify 压缩小程序代码

    小程序的官方压缩,很不如人意,可以用 uglify 对其中的 js 进行混淆压缩.
    参考: uglify-jsuglify-es 文档

    1. 安装 Uglify

    首先安装 uglify:

    npm install uglify-es -D
    

    注意,这里用的是 uglify-es,而不是 uglify-js,原因是 uglify-js 只支持 ECMAScript 5 (ES5),若想压缩 ES2015+ (ES6+)代码,应该使用 uglify-es这个npm 包。
    装好后,可以测试一下是否能正常运行,准备一个 test.js,写入几行测试代码,然后构建一个 package.json,对test.js 进行压缩

    
    {
      "devDependencies": {
        "uglify-es": "^3.3.9"
      },
      "scripts": {
        "start": "uglifyjs test.js"
      }
    }
    
    

    执行 npm start,即可看到控制台输出压缩后代码。

    2. 配置 Uglify API

    创建 uglify.config.js,根据 uglify 文档,做基础配置:

    const UglifyJS = require('uglify-es'),
      fs = require('fs');
    const options = {
      // 解析配置
      parse: {},
      // 压缩配置
      compress: {
        drop_console: true,
      },
      // 混淆配置
      mangle: {},
      // 输出配置
      output: {
        comments: false,    // 移除注释
      },
      sourceMap: {},
      ecma: 8,  // specify one of: 5, 6, 7 or 8
      keep_fnames: false,   // 防止丢弃或损坏函数名
      keep_classnames: false,
      toplevel: false,    // 混淆最高作用域中的变量和函数名
      warnings: false,
    }
    // 读取文件代码
    let code = fs.readFileSync('./test.js', "utf8");
    // uglify 压缩
    let result = UglifyJS.minify(code, options);
    // 写入到指定文件
    fs.writeFileSync('./test.min.js', result.code)
    

    在 package.json 中增加一行命令:"build": "node uglify.config.js"
    然后npm run build执行后,在同级目录下,生成一个 test.min.js。

    接下来要考虑的,是怎么进行工程化。
    单个js文件的压缩简单,但是文件多起来,就没法做到每次压缩都手动进行,所以要考虑做一个遍历,对小程序代码文件夹中的所有文件进行分析,压缩js文件,复制非js文件,最后统一输出到 指定目录下。

    首先是,查询目录,遍历文件:

    
    // 这里引入 path 模块
    const path = require('path');
    
    const handle = (src, dist) => {
      // 解析一个目录,遍历目录下一级所有资源
      //   若是目录则继续解析,
      //   若是文件则判断压缩或者复制
      let paths = fs.readdirSync(src); //查询当前目录下内容
      paths.forEach (p => {
        // 解析输入、输出路径
        let full_src = path.resolve(src, p);
        let full_dist = path.resolve(dist, p);
        // console.log(`>${p}: ${full_src} --> ${full_dist}`)
    
        fs.stat(full_src, (err, stats) => {
          if (err) throw err;
          if (stats.isFile()) {
            // 文件
            if (/.js$/.test(full_src)) {
              console.log('正在压缩js:' + full_src)
              let code = fs.readFileSync(full_src, "utf8");
              let result = UglifyJS.minify(code, options);
              fs.writeFileSync(full_dist, result.code)
            } else {
              let readable = fs.createReadStream(full_src);
              let writable = fs.createWriteStream(full_dist);
              readable.pipe(writable);
            }
          } else if (stats.isDirectory()) { 
            //目录 递归
            checkDirectory(full_src, full_dist, handle);
          }
        });
      });
    }
    const checkDirectory = (src, dist, callback) => {
      // 查询输出目录
      fs.access(dist, fs.constants.F_OK, (err) => {
        if (err) {
          // 不存在此目录,则创建
          fs.mkdirSync(dist);
          // 随后执行目录解析操作
          callback(src, dist);
        } else {
          callback(src, dist);
        }
      });
    };
    
    // 执行
    checkDirectory('./src', './dist', handle);
    

    能运行起来,但存在一个问题,所有的资源都进行了操作。这时候需要一个 ignore 索引:

    
    const ignore = [
      './node_modules',
      './dist',
      './package.json',
      './package-lock.json',
    ]
    const checkIgnore = (src, full_path) => {
      let ignoreList = ignore.map(i => path.resolve(i))
      if (ignoreList.indexOf(src) >= 0) return true;
      if (full_path.indexOf('.') === 0) return true;
      if (/.md$/.test(full_path)) return true;
    }
    

    压缩前,如果能清空输出目录就更清晰了:

    
    const delPath = (url, isRoot = false) => {
      if (/^(/)|^(../)|^(./)$/.test(url)) {
        console.warn("发现危险操作");
        throw new Error("发现危险操作..");
      }
      url = path.resolve(url)
      if (!fs.existsSync(url)) {
        console.warn("路径不存在");
        return "路径不存在";
      }
      let info = fs.statSync(url);
      if (info.isDirectory()) { 
        //目录
        let paths = fs.readdirSync(url);
        // console.log(paths)
        if (paths.length > 0) {
          for (let i = 0; i < paths.length; i++) {
            delPath(`${url}/${paths[i]}`); 
            if (i == paths.length - 1 && !isRoot) { 
              //删掉当前目录
              delPath(`${url}`);
            }
          }
        } else { 
          //删除空目录
          !isRoot && fs.rmdirSync(url),console.log('删除目录:', url)
        }
      } else if (info.isFile()) {
        //删除文件
        fs.unlinkSync(url); 
        // console.log('删除:', url)
      }
    }
    

    然后调整一下配置结构,提取配置项:

    
    const UglifyJS = require('uglify-es'),
      fs = require('fs'),
      path = require('path');
    
    const config = {
      entry: './src',
      output: './dist',
      ignore: [
        './node_modules',
        './dist',
        './index.js',
        './package.json',
        './package-lock.json',
      ],
      options: {
        // 解析配置
        parse: {},
        // 压缩配置
        compress: {
          drop_console: true,
        },
        // 混淆配置
        mangle: {},
        // 输出配置
        output: {
          comments: false,    // 移除注释
        },
        sourceMap: {},
        ecma: 8,  // specify one of: 5, 6, 7 or 8
        keep_fnames: false,   // 防止丢弃或损坏函数名
        keep_classnames: false,
        toplevel: true,    // 混淆最高作用域中的变量和函数名
        warnings: false,
      }
    }
    
    const handle = (src, dist) => {
      // 解析一个目录,遍历目录下一级所有资源
      //   若是目录则继续解析,
      //   若是文件则判断压缩或者复制
      let paths = fs.readdirSync(src); //查询当前目录下内容
      paths.forEach (p => {
        // 解析输入、输出路径
        let full_src = path.resolve(src, p);
        let full_dist = path.resolve(dist, p);
        // console.log(`>${p}: ${full_src} --> ${full_dist}`)
    
        // 判断 ignore
        if (checkIgnore(full_src, p)) return console.log('忽略:', p);
    
        fs.stat(full_src, (err, stats) => {
          if (err) throw err;
          if (stats.isFile()) {
            // 文件
            if (/.js$/.test(full_src)) {
              console.log('正在压缩js:' + full_src)
              let code = fs.readFileSync(full_src, "utf8");
              let result = UglifyJS.minify(code, config.options);
              fs.writeFileSync(full_dist, result.code)
            } else {
              let readable = fs.createReadStream(full_src);
              let writable = fs.createWriteStream(full_dist);
              readable.pipe(writable);
            }
          } else if (stats.isDirectory()) { 
            //目录 递归
            checkDirectory(full_src, full_dist, handle);
          }
        });
      });
    }
    const checkDirectory = (src, dist, callback) => {
      // 查询输出目录
      fs.access(dist, fs.constants.F_OK, (err) => {
        if (err) {
          // 不存在此目录,则创建
          fs.mkdirSync(dist);
          // 随后执行目录解析操作
          callback(src, dist);
        } else {
          callback(src, dist);
        }
      });
    };
    
    const checkIgnore = (src, full_path) => {
      let ignoreList = config.ignore.map(i => path.resolve(config.entry, i))
      if (ignoreList.indexOf(src) >= 0) return true;
      if (full_path.indexOf('.') === 0) return true;
      if (/.md$/.test(full_path)) return true;
    }
    
    const delPath = (url, isRoot = false) => {
      if (/^(/)|^(../)|^(./)$/.test(url)) {
        console.warn("发现危险操作");
        throw new Error("发现危险操作..");
      }
      url = path.resolve(url)
      if (!fs.existsSync(url)) {
        console.warn("路径不存在");
        return "路径不存在";
      }
      let info = fs.statSync(url);
      if (info.isDirectory()) { 
        //目录
        let paths = fs.readdirSync(url);
        // console.log(paths)
        if (paths.length > 0) {
          for (let i = 0; i < paths.length; i++) {
            delPath(`${url}/${paths[i]}`); 
            if (i == paths.length - 1 && !isRoot) { 
              //删掉当前目录
              delPath(`${url}`);
            }
          }
        } else { 
          //删除空目录
          !isRoot && fs.rmdirSync(url),console.log('删除目录:', url)
        }
      } else if (info.isFile()) {
        //删除文件
        fs.unlinkSync(url); 
        // console.log('删除:', url)
      }
    }
    
    // 执行
    delPath(config.output, true)
    checkDirectory(config.entry, config.output, handle);
    

    以及 package.json:

    
    {
      "devDependencies": {
        "uglify-es": "^3.3.9"
      },
      "scripts": {
        "start": "uglifyjs test.js",
        "build": "node uglify.config.js"
      }
    }
    
    

    后续可以针对 wxss、wxml 做一些处理

    未完,待续...

  • 相关阅读:
    《信息安全系统设计基础》实验中的问题总结
    20145233计算机病毒实践7之动态分析3
    20145233计算机病毒实践3之静态分析3
    20145233计算机病毒实践5之动静态分析
    20145233计算机病毒实践2之静态分析
    20145233计算机病毒实践1之静态工具介绍
    20145233计算机病毒实践十
    20145233计算机病毒实践九之IDA的使用
    20145233韩昊辰小组 课程设计中期检查
    20145233《网络对抗》Exp9 Web安全基础实践
  • 原文地址:https://www.cnblogs.com/_error/p/13448279.html
Copyright © 2011-2022 走看看