目前所负责的项目要启动重构了。虽然之前也写过node和webpack的demo,但对整体的框架认知还不够全面,这次在搭建过程中,了解了每一个中间件所发挥的作用。收益很多。并把一些重要的知识点做了总结。
整体思路
多路由多页面应用设计
复制代码
目录结构
.
├── client
│ ├── libs
│ │ └── qn.js
│ ├── npm-scripts
│ │ ├── _prompt.js
│ │ └── dev.js
│ ├── package.json
│ ├── pages
│ │ └── 2019-spokesman
│ ├── webpack
│ │ └── getPlugins.js
│ ├── webpack.dev.js
│ └── webpack.pro.js
├── dist
│ ├── index.js
│ └── index.js.map
├── nodemon.json
├── package.json
├── public
│ ├── dist
│ │ ├── 2019-spokesman-main-9f9d20d4.js
│ │ ├── 2019-spokesman-main-9f9d20d4.js.map
│ │ ├── 2019-spokesman-vendor-8702176e.js
│ │ └── 2019-spokesman-vendor-8702176e.js.map
│ └── favicon.ico
├── server
│ ├── config
│ │ ├── env
│ │ ├── index.ts
│ │ └── qconf.ts
│ ├── index.ts
│ ├── middlewares
│ │ ├── pageNotFound.ts
│ │ ├── registerRouter.ts
│ │ └── wechat.ts
│ ├── routers
│ │ └── demo.ts
│ ├── types
│ │ ├── config.d.ts
│ │ └── wx.d.ts
│ └── wx
│ ├── index.ts
│ └── libs
├── tsconfig.json
└── views
└── 2019-spokesman.hbs
复制代码
server 文件下的入口文件 -- index.ts
-
koa 2+
-
koa-base-logger 日志中间件
-
koa-body post 请求中间件
- post请求数据默认存放在ctx.request.body
app.use(koaBody({ // 是否支持 multipart-formdate 的表单, 默认false不支持 multipart: true, })) 复制代码
-
koa-views 处理模版中间件
app.use(Views(viewsPath, { extension: 'hbs', // 将文件扩展名映射到引擎 map: { hbs: 'handlebars' }, })) 复制代码
-
koa-helmet http请求头,安全性相关设置
-
koa-static 静态文件中间件
const serve = require('koa-static') app.use(serve(path.resolve(__dirname, 'xxx'))) // xxxwenji require('koa-static')(root, org) // 官方解释:root directory string. nothing above this root directory can be served * 安全漏洞:根据静态资源文件地址访问到整个服务器目录 做如下处理。 // 对于method:为get、protocol:为·http协议的请求重定向,禁止恶意的带':'访问 // 问题链接 https://App.blued.cn:443/file%3a///etc/passwd if (/\w*:/i.test(decodeURIComponent(ctx.path))) { ctx.redirect('/common/error/404') return false } 复制代码
-
koa-router 路由中间件
-
koa-compose
-
glob 匹配文件 glob.sync('*.js') 返回当前文件夹下 js文件数组集合
-
对访问地址状态的处理 处理ctx.status 状态为404,403时,重定向页面地址
-
其他配置的中间件
前端页面
webpack 配置
-
css 与 js 分离
import ExtractTextPlugin from 'extract-text-webpack-plugin' 复制代码
- 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。
-
HappyPack
运行在Node.js 上的webpack是单线程模型的。happypack 把任务分解给多个子进程去并发执行,子进程处理完后再把结果发给主进程
- 参数
{ threadPool: // 代表共享进程池,即多个HappyPack实例都使用同一个共享进程池中的子进程去处理任务 verbose // 是否允许happypack 输出日志,默认是true threads // 代表开启几个子进程去处理这一类型的文件,默认是3个,类型必须是整型。 } 复制代码
- 每通过 new HappyPack() 实例化一个 HappyPack 其实就是告诉 HappyPack 核心调度器如何通过一系列 Loader 去转换一类文件,并且可以指定如何给这类转换操作分配子进程。
-
loaders loaders: ['babel-loader?cacheDirectory'] 你也可以通过使用 cacheDirectory 选项,将 babel-loader 提速至少两倍。 这会将转译的结果缓存到文件系统中。
plugins
-
webpack.DefindPlugin
- 因为这个插件直接执行文本替换,给定的值必须包含字符串本身内的实际引号。通常,有两种方式来达到这个效果,使用
"production"
, 或者使用JSON.stringify('production')
。
- 因为这个插件直接执行文本替换,给定的值必须包含字符串本身内的实际引号。通常,有两种方式来达到这个效果,使用
-
html-webpack-plugin 动态注入css js 到html 模版
-
hash chunkhash contenthash
- chunkhash 根据模块内容计算出的hash值
- contenthash代表的是文本文件内容的hash值
动态生成script 命令
- inquirer 交互式命令工具
inquirer.prompt(questions).then(({...}) => { }) 传递给prompt方法的参数为一个question问题数组 复制代码
question = [ { type: String, // 表示提问的类型,下文会单独解释 name: String, // 在最后获取到的answers回答对象中,作为当前这个问题的键 message: String|Function, // 打印出来的问题标题,如果为函数的话 default: String|Number|Array|Function, // 用户不输入回答时,问题的默认值。或者使用函数来return一个默认值。假如为函数时,函数第一个参数为当前问题的输入答案。 choices: Array|Function, // 给出一个选择的列表,假如是一个函数的话,第一个参数为当前问题的输入答案。为数组时,数组的每个元素可以为基本类型中的值。 validate: Function, // 接受用户输入,并且当值合法时,函数返回true。当函数返回false时,一个默认的错误信息会被提供给用户。 filter: Function, // 接受用户输入并且将值转化后返回填充入最后的answers对象内。 when: Function|Boolean, // 接受当前用户输入的answers对象,并且通过返回true或者false来决定是否当前的问题应该去问。也可以是简单类型的值。 pageSize: Number, // 改变渲染list,rawlist,expand或者checkbox时的行数的长度。 }] 复制代码
- ora 角标
require 与 import 的区别
commonJs 规范 以modules.exports 导出接口 以require 引入模块
modules.exports 只是node 私有的一个全局变量属性,跟标准半毛钱关系都没有
- export 导出模块接口
// 错误演示
export 1; // 绝对不可以
var a = 100;
export a;
复制代码
export 在导出接口的时候,必须与模块内部的变量具有一一对应的关系。直接导出1没有任何意义,也不可能在import的时候有一个变量与之对应。export 虽然看上去成立,但是a的值是一个数字,根本无法完成解构,因此必须写成export {a} 的形式,即使a被赋值为一个function ,也是不允许的。 而且,大部分风格都建议,模块中最好在末尾用一个export导出所有的接口。例如:
export {fun as default, a, b, c}
复制代码
- import导入模块 import 必须放在文件的最开始,且
前面不允许有其他逻辑代码
import 的使用和export一样,也挺复杂,可以在这里大致了解。
import $ from 'jquery';
import * as _ from '_';
import {a,b,c} from './a';
import {default as alias, a as a_a, b, c} from './a';
复制代码
import 后面跟上花括号的形式是最基本的用法,花括号里面的变量与export后面的变量一一对应。
复制代码
-
as关键字
var a = function() {}; export {a as fun}; // b.js import {fun as a} from './a'; a(); 复制代码
export的时候,对外提供的接口是fun,它是a.js 内部a这个函数的别名,但是在模块外面,认不到a ,只能认到fun import 中的as就很简单,就是你在使用模块里面的方法的时候,给这个方法取个别名。
-
default 关键字 别名的语法糖 import 的时候,可以省去花括号,简单的说,如果import的时候,你发现某个变量没有花括号括起来(没有*号),那么你的脑海中应该把它还原成有花括号的as语法。
import a,{each,map} from 'jquery' 复制代码
- import 后面第一个a是
{default as a}
的语法糖
- import 后面第一个a是
-
*符号
*就是代表所有,只用在import 中,
import * as _ from '_' 复制代码
在意义上和
import _ from '_'
是不同的,它表示的是把'_'模块中的所有接口挂载到 _ 这个对象上,所以可以用_.each
调用某个接口另外还可以通过*号直接继承某一个模块的接口
export * from '_' // 等效于 import * as all from '_' export all; 复制代码
*符号尽可能少用,它实际上是使用所有export的接口,但是很有可能你的当前模块并不会用到所有接口,所以最好的建议是使用花括号,用一个加一个
-
该用require还是import
require的使用非常简单,它相当于module.exports 的传送门,module.exports后面的内容是什么,require的结果就是什么,对象、数字、字符串、函数......再把require 的结果赋值给某个变量,相当于把require和module.exports 进行平行空间的位置重叠。 其实还是没太整明白什么时候用import 什么时候用require