https://github.com/gyson/koa-simple-router
-
将YII文件夹复制过来,主要学习MVC框架结构
image.png
文件结构如下:
wigdets = components都是组件的意思。
image.png
然后删除掉一些不需要用到的文件夹。
assert里面是js css目录
config里面是配置文件
controller里面是路由
models和后台请求有关(通信)
将runtime更改为middleware,放中间件
tests文件夹存放单元测试文件。
views存放视图文件
web存放前端的项目(前后端分离的时候)
widgets改为components存放组件。 -
controller里面新建index.js(路由的初始化)
index.js就是我们的路由注册中心
安装koa-simple-router
npm install koa-simple-router
将demo 里面的代码复制过去并且修改module.exports = (app) => { app.use(router(_ => { _.get('/', (ctx, next) => { ctx.body = 'hello' }) })) }
但是我们需要把一些具体的操作都独自放在一个控制器里面,用class来控制。
所以,新建IndexController。
class IndexController{
constructor(){
}
actionIndex(){
return async(ctx, next) => {
ctx.body = 'hello 大家好'
}
}
}
module.exports = IndexController;
定义 actionIndex并将IndexController导出。
此时,路由初始化文件可以更改为:
const router = require('koa-simple-router')
const IndexController = require('./IndexController');
const indexController = new IndexController();
module.exports = (app) => {
app.use(router(_ => {
_.get('/',indexController.actionIndex())
}))
}
app.js中的代码如下:
const Koa = require('Koa');
const app = new Koa();
//注入我们的路由
require('./controllers')(app);
console.log('服务已启动');
app.listen(3000);
启动服务,此时就可以看到
image.png
- 每次更改都要手动去重启服务,很麻烦,在node中可以使用supervisor进行设置,所以我们可以直接在package.json里面的script进行设置。
cross-env。
能够提供一个设置环境变量的scripts,让你能够以unix方式设置环境变量,然后在windows上也能兼容运行。
https://blog.csdn.net/qq_26927285/article/details/78105510
他是运行跨平台设置和使用环境变量的脚本
是因为windows不支持NODE_ENV=development的设置方式。会报错,所以cross-env能够提供一个设置环境变量的scripts,让你能够以unix方式设置环境变量,然后在windows上也能兼容运行。
而且这个也能判断生产环境还是开发环境,后续能进行流清洗。
安装
cnpm install cross-env --save
使用
"dev": "cross-env NODE_ENV=development supervisor app.js"
设置当前环境是development,然后再执行supervisor app.js
执行之后就可以看见process.env.NODE_ENV打印出development
下面是package.json的标准教程
http://javascript.ruanyifeng.com/nodejs/packagejson.html#toc0
-
新建views视图
view中新建index.html和test.html
在layout中,对应的引入script和css文件应该是这样的
image.png
一一对应的渲染,index.html继承于layout.html
swig模板的使用
Swig 使用 extends 和 block 来实现模板继承 layout.html
layout.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
{% block head %}{% endblock %}
<!-- block head的位置是留给CSS -->
</head>
<body>
{% block content %}{% endblock %}
<!-- block content的位置是留给html的 -->
{% block scripts %}{% endblock %}
<!-- block scripts的位置是留给js的 -->
</body>
</html>
index.html
{% extends './layout.html' %}
{% block title %}新闻系统 {%endblock%}
{% block head %}
<link rel = "stylesheet" href="/styles/index.css">
{% endblock %}
{% block content %}
{% include "../widgets/news/list.html" %}
{% endblock %}
{% block scripts %}
<script src = "/scripts/index.js"></script>
{% endblock %}
http://www.iqianduan.net/blog/how_to_use_swig
koa2-connect-history-api-fallback
https://www.npmjs.com/package/koa2-connect-history-api-fallback
koa2的一个中间件,用于处理vue-router使用history模式返回index.html,让koa2支持SPA应用程序。
https://www.npmjs.com/package/koa-swig
npm install koa-swig
cnpm install co(koa2.x需要加载co模块使得yield不是yield)
https://www.npmjs.com/package/koa-static
npm install koa-static
加载静态资源文件的。
- 容错
利用洋葱模型的特性来做Koa的容错。
在中间件中建立errorHandler.js
如果是404,就接入腾讯404小孩回家页面
https://www.qq.com/404/
const errorHandler = {
error(app){
app.use(async (ctx,next) => {
await next();
//先让代码往前走
if(404 != ctx.status){
return
}
//不承认网站404,百度会降权,将ctx.status修改为200,并修改ctx.body为找不到页面的页面
//腾讯404脚本
ctx.status = 404;
ctx.body = '<script type="text/javascript" src="//qzonestyle.gtimg.cn/qzone/hybrid/app/404/search_children.js" charset="utf-8"></script>';
})
}
}
module.exports = errorHandler;
app.js
const errorHandler = require('./middleware/errorHandler');
在路由之前加上
errorHandler.error(app);
如果去营造服务器错误??就应该执行一个不存在的函数,这样就不是在编译时出错,而是可以跑起来。
在errorHandler.js里面增加
app.use(async (ctx,next) => {
try{
await next();
}catch(error){
ctx.status = 500;
console.log(error);
ctx.body = '( ▼-▼ )';
}
});
在indexController.js里面添加一个不存在的函数(例如我们没有定义indexAction()),那么就会出现500错误,是服务器这边的错误。
这个时候在localhost里面可以看到( ▼-▼ )
在命令行可以看到下面的提示indexAction is not defined
ReferenceError: indexAction is not defined
at C:\document\frontEnd\京城一灯\note\0204homework_second_week\controllers\IndexController.js:8:13
at dispatch (C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-simple-router@0.2.0@koa-simple-router\index.js:186:18)
at Router._lookup (C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-simple-router@0.2.0@koa-simple-router\index.js:198:12)
at C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-simple-router@0.2.0@koa-simple-router\index.js:138:21
at dispatch (C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-compose@4.1.0@koa-compose\index.js:42:32)
at app.use (C:\document\frontEnd\京城一灯\note\0204homework_second_week\middleware\errorHandler.js:13:19)
at dispatch (C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-compose@4.1.0@koa-compose\index.js:42:32)
at app.use (C:\document\frontEnd\京城一灯\note\0204homework_second_week\middleware\errorHandler.js:5:23)
at dispatch (C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-compose@4.1.0@koa-compose\index.js:42:32)
at serve (C:\document\frontEnd\京城一灯\note\0204homework_second_week\node_modules_koa-static@5.0.0@koa-static\index.js:53:15)
error的捕获是特别重要的,最好可以打印出日志。
log4js
https://www.npmjs.com/package/log4js
在服务器上面也应该打印出日志。
-
先安装log4js
app.js里面定义const log4js = require('log4js');
再添加配置
log4js.configure({
appenders: {
cheese: {
type: 'file', filename: 'cheese.log'
}
},
categories: {
default: {
appenders: ['cheese'], level: 'error'
}
}
});
const logger = log4js.getLogger('cheese');
errorHandler.error(app,logger);
此时,在errorHandler里面添加logger
error(app,logger){
app.use(async (ctx,next) => {
try{
await next();
}catch(error){
ctx.status = 500;
//console.log(error);
logger.error(error);
ctx.body = '( ▼-▼ )';
}
});
将error打印出来。
- model和php连接
在model里面新建一个index.js
新建一个utils文件夹,工具包的意思。
为什么要注释?因为要输出文档,我们可以用jsdoc来生成相关的说明文档,避免撕逼
https://www.npmjs.com/package/jsdoc
-
安装
cnpm install --save-dev jsdoc -
在package.json里面的scripts里面加上一个命令
"docs": "jsdoc ./**/*.js -d ./docs/jsdocs"
执行npm run docs
在linux系统上面的**是可以起作用的,但是在windows上面是不起作用的。
所以改成下面:
"docs": "jsdoc ./models/index.js -d ./docs/jsdocs"
才可以在window上面跑。
执行npm run docs,可以看到docs目录下面生成了一个jsdocs的文件夹,打开index_.html用浏览器打开就可以看到文档了。
controller可以不写文档,但是model就要写文档。
论写docs邀功的重要性
在安装依赖的时候注意到了一个问题:
npm install 在安装 npm 包时,有两种命令参数可以把它们的信息写入 package.json 文件,一个是npm install--save另一个是 npm install –save-dev,他们表面上的区别是--save 会把依赖包名称添加到 package.json 文件 dependencies 键下,--save-dev 则添加到 package.json 文件 devDependencies 键下,譬如:
{
"dependencies": {
"vue": "^2.2.1"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-lo
}
}
它们真正的区别是,npm自己的文档说dependencies是运行时依赖,devDependencies是开发时的依赖。即devDependencies 下列出的模块,是我们开发时用的,比如 我们安装 js的压缩包gulp-uglify 时,我们采用的是 “npm install –save-dev gulp-uglify ”命令安装,因为我们在发布后用不到它,而只是在我们开发才用到它。dependencies 下的模块,则是我们发布后还需要依赖的模块,譬如像jQuery库或者Angular框架类似的,我们在开发完后后肯定还要依赖它们,否则就运行不了。
另外需要补充的是:
正常使用npm install时,会下载dependencies和devDependencies中的模块,当使用npm install –production或者注明NODE_ENV变量值为production时,只会下载dependencies中的模块。
models下面的index.js代码如下:
/**
* @fileoverview 实现index的数据模型
* @author zty
*/
const SafeRequest = require('../utils/SafeRequest.js')
/**
* Index类 获取后台关于图书相关的数据类
* class
*/
class Index{
/**
* @constructor
* @param{string} app 参数是字符串,是Koa执行上下文
*/
constructor(app){
}
/**
* 获取后台全部图书的数据方法
* @param{*} option 配置项
* @example
* return new Promise
* getData(options)
*/
getData(options){
return {};
}
}
module.exports = Index;
一直把ladash拼错了尴尬。
-
多个路由的配置
在controller里面新建一个文件TestController,类名与文件名称相对应class TestController{ constructor(){ } actionIndex(){ return async(ctx, next) => { //ctx.body = 'hello 我是zty' ctx.body = await ctx.render("index",{ data:'jjjj' }) } } } module.exports = TestController; //将模块暴露出去。
然后在路由的注册中心index.js上面引入,并对新路由进行定义。
const TestController = require('./TestController'); const testController = new TestController(); module.exports = (app) => { app.use(router(_ => { _.get('/',indexController.actionIndex()) })) app.use(router(_ => { _.get('/test',testController.actionIndex()) })) }
之后输入相对应的路由就能到不同的页面了
-
生成JSON接口
在我们之前的yii项目里面的controller里面的libaryController进行更改就行了。/** * Lists all Library models. * @return mixed */ public function actionIndex() { $searchModel = new LibrarySearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams); //将输出指定为JSON格式 Yii::$app->response->format = Response::FORMAT_JSON; return $dataProvider->getModels(); }
重点就在于
Yii::$app->response->format = Response::FORMAT_JSON;
return $dataProvider->getModels();
看到了一个错误:Class 'app\controllers\Response' not found
原来是没有引入这个东西
应该在libaryController头部加上
use yii\web\Response;
然后就没有问题了。
之后在浏览器地址栏输入http://localhost/Yii/test3/web/index.php?r=library
library对应LibrartController这个控制器
-
容错
安装node-fetchcnpm install node-fetch --save
https://www.npmjs.com/package/node-fetch
继续编辑utils中SafeRequest,对一些请求进行容错。
const fetch = require("node-fetch");
const config = require("../config");
//设置传输进来的url和baseURL
class SafeRequest{
constructor(url){
this.url = url;
this.baseURL = config.baseURL;
}
fetch(options){
let ydfetch = fetch(this.baseURL + this.url);
return new Promise((resolve,reject) =>{
let result = {
code:0,
message:"",
data:[]
}
ydfetch
.then(res => res.json())
.then((json) => {
result.data = json;
resolve(result);
}).catch((error) => {
result.code = 1;
result.message = "node-fetch与后端通讯异常";
})
})
}
}
module.exports = SafeRequest;
将indexController中的actionIndex修改为
const Index = require("../models/index");
class IndexController{
constructor(){
}
actionIndex(){
//SSR
return async(ctx, next) => {
const index = new Index();
const result = await index.getData();
ctx.body = await ctx.render("index",{
// data: '欢迎来到新世界'
data:result.data
})
}
}
}
module.exports = IndexController;
将取得的data渲染出来。
好尴尬,把assets写错了
-
在views里面添加add.html文件(swig模板)
只有这个add.html需要用到vue,所以在add.html的swig模板里面引入这个src。{% extends './layout.html' %} {% block title %}新增新闻页面{%endblock%} {% block head %} <link rel="stylesheet" type="text/css" href="/styles/index.css" /> {% endblock %} {% block content %} {% include "../components/news/add.html" %} {% endblock %} {% block scripts %} <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src = "/scripts/add.js"></script> {% endblock %}
在components里面添加add.html文件(纯纯的html文件)
<div>
<p>新增新闻页面</p>
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
</div>
add.js代码
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
但是这里的swig和vue的{{}}冲突了,所以我们的p不能被渲染出来
这个时候就可以在app.js里面进行设置了,在app.context.render里面加上
varControls:["[[","]]"],
将swig中的渲染{{}}改成[[ ]],
http://www.staticfile.org/
里面存放了很多开源的库
可以搜索你想要的一些CDN资源,jquery等
还有一个问题
V8对一些新的API都是有优化的,比如map,set等
如果不管三七二十一就把ES6代码用babel转换为ES5代码,那就白瞎了敲那些ES6代码
所以就可以利用type=module来进行判断,如果支持的话就可以直接加载ES6模块不然就可以加载babel转换的ES5模块。
https://babeljs.io/repl(babel相关的网站)
浏览器加载ES6模板,也是使用<script>标签,但是要加入type = “module”属性。
<script type= 'module' src = "foo.js"> </script>
网页中插入一个模块foo.js,由于type属性设为module,所以浏览器知道这是一个 ES6 模块。
浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
https://www.imooc.com/article/20630?block_id=tuijian_wz
<script type="module" src="foo.js"></script>
<script type="module" src="foo.js" defer></script>
所以,我们可以这样解决这个问题:
<script type="module">
import("http://localhost:3000/scripts/add.js").then((_) =>{
new _.default.fn()
})
</script>
<script type="nomodule" src="scripts/add-bundle.js">
</script>
其中add-bundle.js就是我们用Babel编译之后的文件。
swig一段时间内会缓存模板,
如果在调试的时候需要不保留缓存,应该怎么做?
打开app.js文件
我们就会看到
image.png
这里面的cache的memory就是缓存,
在config里面进行设置,在这里我们设置为
cache: config.cacheModel
image.png
system.js与微前端
https://www.javascriptcn.com/read-32574.html
微前端:
https://blog.csdn.net/qappleh/article/details/80928434
还有@babel/plugin-transform-modules-systemjs这个东西
https://www.npmjs.com/package/@babel/plugin-transform-modules-systemjs
让babel支持systemjs
add.html
{% extends './layout.html' %}
{% block title %}新增新闻页面{%endblock%}
{% block head %}
<link rel="stylesheet" type="text/css" href="/styles/index.css" />
{% endblock %}
{% block content %}
{% include "../components/news/add.html" %}
{% endblock %}
{% block scripts %}
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
<script src="https://cdn.staticfile.org/jquery/3.3.1/jquery.js"></script>
<script type="module">
console.log("我支持module");
import("/scripts/add.js").then((_)=>{
const create = new _.default;
create.fn();
})
</script>
{% endblock %}
add.js
class Create{
constructor(){
this.btn = $("#js-btn");
}
fn(){
this.btn.click(function(){
fetch("添加新闻页面");
})
}
}
export default Create;
-
函数式编程之防抖动
针对一些会频繁触发的事件如scroll、resize,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能。因此针对这类事件要进行防抖动或者节流处理
防抖动它的做法是限制下次函数调用之前必须等待的时间间隔。正确实现 debouncing 的方法是将若干个函数调用合成 一次,并在给定时间过去之后仅被调用一次。
underscore.js
https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001450372452505599881a3debd4becbe1591a94950fbb8000
出现了这个错
image.png
原来是参数写错位置了。
此时的add.js应该是这样的
class Create{
constructor(){
this.btn = $("#js-btn");
}
fn(){
this.btn.click(common.throttle(function(){
fetch("添加新闻页面");
},2000))
}
}
common.js作为全局的js,在layout.html就引入了
common.js
function common(){}
common._version = 0.1;
common.throttle = function(fn,wait){
var timer;
return function (...args){
if(!timer){
timer = setTimeout(()=>timer = null,wait);
//方便改变this指向,改变更加灵活
return fn.apply(this,args);
}
}
}
export default Create;
验证之后发现防抖是有用的。~~
-
rize.js
https://github.com/g-plane/rize
https://rize.js.org/zh-CN/#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B -
node 跑post接口
在safeRequest中,添加对option参数的判断fetch(options){ let ydfetch = fetch(this.baseURL + this.url); if(options.params){ ydfetch = fetch(this.baseURL + this.url,{ method:params.method, body:options.params }) } }
在model中的index.js中要添加一个saveData的方法。
saveData(options){
//create对应actionCreate(YII中的控制器里面的)
const safeRequest = new SafeRequest("library/create");
return safeRequest.fetch({
method:POST,
params:options.params
});
}
fetch:
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
网友评论