【第562期】用 webpack 构建 node 后端代码,使其支持 js 新特性并实现热重载

前言
ok,昨天那篇文章看完有什么感受呢?今天的文章又是比较高大上的,来自美信FED前端团队的@luoye童鞋投稿的。

正文从这开始~

webpack 在前端领域的模块化和代码构建方面有着无比强大的功能,通过一些特殊的配置甚至可以实现前端代码的实时构建、ES6/7新特性支持以及热重载,这些功能同样可以运用于后台 nodejs 的应用,让后台的开发更加顺畅,服务更加灵活,怎么来呢?往下看。

先梳理下我们将要解决的问题:

  • node端代码构建

  • ES6/7 新特性支持

  • node服务代码热重载

node端代码构建

node端的代码其实是不用编译或者构建的,整个node的环境有它自己的一个模块化或者依赖机制,但是即使是现在最新的node版本,对ES6/7的支持还是捉襟见肘。当然使用一些第三方库可以做到支持类似async/await 这样的语法,但是毕竟不是规范不是标准,这样看来,node端的代码还是有构建的需要的。这里我们选取的工具就是 webpack 以及它的一些 loader

首先,一个 node app 必定有一个入口文件 app.js ,按照 webpack 的规则,我们可以把所有的代码打包成一个文件 bundle.js ,然后运行这个 bundle.js 即可,webpack.config.js如下:

var webpcak = require('webpack');module.exports = {
    entry: [        './app.js'
    ],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    }
}

但是有一个很严重的问题,这样打包的话,一些 npm 中的模块也会被打包进这个 bundle.js,还有 node 的一些原生模块,比如 fs/path 也会被打包进来,这明显不是我们想要的。所以我们得告诉 webpack,你打包的是 node 的代码,原生模块就不要打包了,还有 node_modules 目录下的模块也不要打包了,webpack.config.js 如下:

var webpcak = require('webpack');var nodeModules = {};
fs.readdirSync('node_modules')
    .filter(function(x) {        return ['.bin'].indexOf(x) === -1;
    })
    .forEach(function(mod) {
        nodeModules[mod] = 'commonjs ' + mod;
    });module.exports = {
    entry: [        './app.js'
    ],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    },
    target: 'node',
    externals: nodeModules
}

主要就是在 webpack 的配置中加上 target: 'node' 告诉 webpack打包的对象是 node 端的代码,这样一些原生模块 webpack 就不会做处理。另一个就是 webpack 的 externals 属性,这个属性的主要作用就是告知 webpack 在打包过程中,遇到 externals 中声明的模块不用处理。

比如在前端中, jQuery 的包通过 CDN 的方式以 script 标签引入,如果此时在代码中出现 require('jQuery') ,并且直接用 webpack 打包比定会报错。因为在本地并没有这样的一个模块,此时就必须在 externals 中声明 jQuery 的存在。也就是 externals中的模块,虽然没有被打包,但是是代码运行是所要依赖的,而这些依赖是直接存在在整个代码运行环境中,并不用做特殊处理。

在 node 端所要做的处理就是过滤出 node_modules 中所有模块,并且放到 externals中。

这个时候我们的代码应该可以构建成功了,并且是我们期望的形态,但是不出意外的话,你还是跑不起来,因为有不小的坑存在,继续往下看。

  • 坑1:__durname __filename 指向问题

    打包之后的代码你会发现 __durname __filename 全部都是 / ,这两个变量在 webpack 中做了一些自定义处理,如果想要正确使用,在配置中加上

    context: __dirname,
    node: {
      __filename: false,
      __dirname: false},
  • 坑2:动态 require 的上下文问题

    这一块比较大,放到后面讲,跟具体代码有关,和配置无关

  • 坑n:其它的还没发现,估摸不少,遇到了谷歌吧…

ES6/7 新特性支持

构建 node 端代码的目标之一就是使用ES6/7中的新特性,要实现这样的目标 babel 是我们的不二选择。

首先,先安装 babel 的各种包 npm install babel-core babel-loader babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-0 --save-dev json-loader -d  

然后修改 webpack.config.js ,如下:

var webpcak = require('webpack');var nodeModules = {};
fs.readdirSync('node_modules')
    .filter(function(x) {        return ['.bin'].indexOf(x) === -1;
    })
    .forEach(function(mod) {
        nodeModules[mod] = 'commonjs ' + mod;
    });module.exports = {
    entry: [        './app.js'
    ],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    },
    target: 'node',
    externals: nodeModules,
    context: __dirname,
    node: {
        __filename: false,
        __dirname: false
    },    module: {
        loaders: [{
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: [
                path.resolve(__dirname, "node_modules"),
            ],
            query: {
                plugins: ['transform-runtime'],
                presets: ['es2015', 'stage-0'],
            }
        }, {
            test: /\.json$/,
            loader: 'json-loader'
        }]
    },
    resolve: {
        extensions: ['', '.js', '.json']
    }
}

主要就是配置 webpack 中的 loader ,借此来编译代码。

node服务代码热重载

webpack 极其牛叉的地方之一,开发的时候,实时的构建代码,并且,实时的更新你已经加载的代码,也就是说,不用手动去刷新浏览器,即可以获取最新的代码并执行。

这一点同样可以运用在 node 端,实现即时修改即时生效,而不是 pm2 那种重启的方式。

首先,修改配置文件,如下:

entry: [    'webpack/hot/poll?1000',    './app.js'],// ...plugins: [    new webpack.HotModuleReplacementPlugin()
]

这个时候,如果执行 webpack --watch & node app.js ,你的代码修改之后就可以热重载而不用重启应用,当然,代码中也要做相应改动,如下:

var hotModule = require('./hotModule');// do something else// 如果想要 hotModule 模块热重载if (module.hot) {    module.hot.accept('./hotModule.js', function() {        var newHotModule = require('./hotModule.js');        // do something else
    });
}

思路就是,如果需要某模块热重载,就把它包一层,如果修改了,webpack 重新打包了,重新 require 一遍,然后代码即是最新的代码。

当然,如果你在某个需要热重载的模块中又依赖另一个模块,或者说动态的依赖了另一个模块,这样的模块并不会热重载。

webpack 动态 require

动态 require 的场景包括:

  • 场景一:在代码运行过程中遍历某个目录,动态 reauire,比如

      //app.js
      var rd = require('rd');  // 遍历路由文件夹,自动挂载路由
      var routers = rd.readFileFilterSync('./routers', /\.js/);
      routers.forEach(function(item) {      require(item);
      })

    这个时候你会发现 './routers' 下的require都不是自己想要的,然后在 bundle.js 中找到打包之后的相应模块后,你可以看到,动态 require 的对象都是 app.js 同级目录下的 js 文件,而不是 './routers' 文件下的 js 文件。为什么呢?

    webpack 在打包的时候,必须把你可能依赖的文件都打包进来,并且编上号,然后在运行的时候 require 相应的模块 ID 即可,这个时候 webpack 获取的动态模块,就不再是你指定的目录'./routers' 了,而是相对于当前文件的目录,所以,必须修正 require 的上下文,修改如下:

      // 获取正确的模块
      var req = require.context("./routers", true, /\.js$/);  var routers = rd.readFileFilterSync('./routers', /\.js/);
      routers.forEach(function(item) {      // 使用包涵正确模块的已经被修改过的 `require` 去获取模块
          req(item);
      })
  • 场景二:在 require 的模块中含有变量,比如

      var myModule = require(isMe ? './a.js' : './b.js');  // 或者
      var testMoule = require('./mods' + name + '.js');

    第一种的处理方式在 webpack 中的处理是把模块 ./a.js ./b.js 都包涵进来,根据变量不同 require 不同的模块。

    第二种的处理方式和场景一类似,获取 ./mods/ 目录下的所有模块,然后重写了 require ,然后根据变量不同加载不通的模块,所以自己处理的时候方法类似。

用 ES6/7 写 webpack.config.js

项目都用 ES6/7 了,配置文件也必须跟上。

安装好 babel 编译所需要的几个依赖包,然后把 webpack.config.js 改为 webpack.config.babel.js ,然后新建 .babelrc 的 babel 配置文件,加入

{
  "presets": ["es2015"]}

然后和往常一样执行 webpack 的相关命令即可。

完整 webpack.config.babel.js 如下:

import webpack from 'webpack';
import fs from 'fs';
import path from 'path';let nodeModules = {};
fs.readdirSync('node_modules')
    .filter((x) => {        return ['.bin'].indexOf(x) === -1;
    })
    .forEach((mod) => {
        nodeModules[mod] = 'commonjs ' + mod;
    });

export default {
    cache: true,
    entry: [        'webpack/hot/poll?1000',        './app.js'
    ],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    },
    context: __dirname,
    node: {
        __filename: false,
        __dirname: false
    },
    target: 'node',
    externals: nodeModules,    module: {
        loaders: [{
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: [
                path.resolve(__dirname, "node_modules"),
            ],
            query: {
                plugins: ['transform-runtime'],
                presets: ['es2015', 'stage-0'],
            }
        }, {
            test: /\.json$/,
            loader: 'json-loader'
        }]
    },
    plugins: [        new webpack.HotModuleReplacementPlugin()
    ],
    resolve: {
        extensions: ['', '.js', '.json']
    }
}

大致流程就是如此,坑肯定还有,遇到的话手动谷歌吧~


原文地址:https://blog.51cto.com/15080028/2595051

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


一准备工作umi为react的一个封装比较nice的脚手架。优点当然就是安装方便,开箱即用,集成了诸多好用功能,详见官网。当然缺点就是无法暴露原始的webpack.config.json文件,只能按照官方文档上特定的方法进行修改,而文档却并不完善。项目顺利进行大家笑嘻嘻,一旦遇到偏难怪需求,
webpack在引入两个依赖的包时,可能需要使用shimming,意思是处理代码上的兼容1、在main.js中引入jqueryimport$from'jquery'importappendfrom'./append'//下面的给页面添加元素文件append()2、append.js使用main.js的$向页面中添加元素functionappDomFunc(){
  Happypack (plugin)多线程使用loader编译文件treeshaking删除无用JS代码,依赖ESM规范source-map生产环境一定去掉process.env.XXX配置环境变量,区分各种编译环境splitchunks 代码分离去重DynamicImport动态加载js文件Hot-module-replacement开发环境热更新w
一目录结构├──build//构建相关├──config//配置相关├──src//源代码│├──api//所有请求│├──assets//主题字体等静态资源│
接着第一节的demo现在我们尝试整合一些其他资源,比如图像,看看webpack如何处理。在webpack出现之前,前端开发人员会使用grunt和gulp等工具来处理资源,并将它们从 /src 文件夹移动到 /dist 或 /build 目录中。同样方式也被用于JavaScript模块,但是,像webpack这样的工
webpack的打包原理识别入口文件通过逐层识别模块依赖(Commonjs、amd或者es6的import,webpack都会对其进行分析,来获取代码的依赖)webpack做的就是分析代码,转换代码,编译代码,输出代码最终形成打包后的代码什么是loaderloader是文件加载器,能够加载资源文件,并对这些文件进行
上一篇文章我们在一个demo中见到了webpack的身影,如果从未接触过webpack的同学学完了上一篇文章可能会觉得webpack只是一个“翻译官”,但事实上webpack可不仅仅可以‘翻译’代码,事实上,webpack是一个‘打包’工具,‘打包‘才是webpack的核心任务。打开webpack的官网:webpack中
注:本章将配合Vue详细讲解webpack前提条件1.在开始之前,请确保安装了Node.js和npm的最新版本。使用旧版本,你可能遇到各种问题,因为它们可能缺少webpack功能以及/或者缺少相关package包。在cmd中node-v,npm-v可查看对应的版本和安装情况。2.这里会用到ES6的模块化,如果你
前言、之前我认为对于项目的优化无非是从代码上去优化一些东西,比如循环呀函数式调用呀让你的代码看起来更加的简洁容易懂后来我在面试过程中不断有面试官不断地问了我这些问题所以自己就去研究了一下发现并不是我之前想的那样,一个好的webapck优化的胜过于你在整体代码上
##一、组件component###1.什么是组件?组件(Component)是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码组件是自定义元素(对象)###2.定义组件的方式方式1:先创建组件构造器,然后由组件构造器创建组件方式2:直接创建组件###3.组件的分
 #webpack5概述>webpack是一个现代javascript应用程序的**静态模块打包器(modulebundler)**>>vue-cli脚手架环境,集成了webpack,所以才能对各类文件进行打包处理[webpack官网](https://webpack.js.org/) ##webpack能做什么webpack是一个静态模块打包
//这里导入webpack配置,我用对象的形式表示,当然entry属性上定义的文件要有//constconfig=require("./webpack.config");constconfig={entry:'./src/index.js'};const{join,dirname}=require("path");const{readFileSync,writeFileSync}=requi
安装cnpminstallwebpack-bundle-analyzer-D 修改vue.config.jsmodule.exports={chainWebpack:config=>{if(process.env.use_analyzer){//分析config.plugin('webpack-bundle-analyzer')
webpack打包html插件html-webpack-plugin的使用1.创建这样的测试目录结构1)index.html是即将被打包的文件,你可以随便写一些内容2)index.js是打包的入口文件,你可以写或不写内容2.webpack.config.js的代码如下:/***压缩html需要插件:html-webpack=plugin*插件使用方法:
vueinitwebpacktabbar创建vue项目时出现问题vue:无法加载文件C:\Users\dengqian\AppData\Roaming\npm\vue.ps1,因为在此系统上禁止运行脚本。C:\Windows\System32\WindowsPowerShell\v1.0,找到如上路径,以管理员身份运行powershell.exe即可输入set-ExecutionPolicyRemo
在webpack中使用ECharts【官网教程】1、使用如下命令通过npm 安装EChartsnpminstallecharts--save2、全局引用在main.jsimportechartsfrom'echarts'Vue.prototype.$echarts=echarts;3、运用<divstyle="width:60vw;height:280px;"ref="chart"&
如果没有看过我的上一篇文章,请先移步去看一下哈!时隔一年多,之前写的文章感觉大家还挺喜欢的,一直说要更新下一篇。但是一直没有机会,没时间,还有就是,感觉自己的技术,可能不足以支持我继续往下走。但是经过这个一年多的历练,感觉很多东西考虑的会更周全,不负期待!废话说一堆,步入正题
dist文件配置对应的路由名展示这样的效果可以创建一个本地的服务还可以进行代理项目开发是在src里进行代码逻辑编写./表示本地路径/表示绝对路径(nginx需要配置)
 1、用命令创建webpack模板项目vueinitwebpack 项目名,需要选是否的统一选否,所以的依赖手动添加  2、安装依赖vue-router,elementui,sass-loader,node-sass,axios插件安装路由:npminstallvue-router安装elementu
最近想把蘑菇博客部署到k8s上,作为一名java搬砖工,搬砖人不讲码德,biu一下就把后端各模块的dockerfile和chart包copy过来了,很快啊!接下来就可以愉快的使用helm管理蘑菇后端服务部署了。部署完后端服务后,准备制作前端镜像,发现前端打包后,无法动态读取系统环境变量,这很头疼,难不