参考:http://blog.csdn.net/kevin_1025745654/article/details/45815109
MV*
已知:M(数据与数据逻辑,例如ajax、本地存储模块)
后端的MVC:M提供数据API;V 即模板,可以直接从M获取数据,C 即控制显示哪个模板
前端的MV*
MVC : V(视图逻辑,backbone里的V即View模块),C(控制视图的显示,backbone里的C即Router模块)
MVP : P(Presenter主持人,或者Passive View被动视图。负责响应V,调用M,更新V)。也可以把backbone的View理解为P。
MVP与MVC的差别:MVC里的V直接调用M的接口,MVP中则不能,得通过中间人P。
MVVM : V(模板),VM(视图模型,即数据与视图绑定,数据逻辑与视图逻辑绑定。普通的M的对外提供接口,VM则自动绑定视图。angularJS里VM即controller组件,里面的逻辑即数据逻辑,里面的scope即数据)
MVVM与MVP的差别:P要显式操作V,而VM与V双向绑定。
优势:代码结构更好(易维护,易打包),双向绑定和组件化(易开发,易协同)
MV*的区别在于V与M之间的流程,MVC中V直接调用M。MVP中V调用P,P再调用M。MVVM中V绑定VM,VM调用M。
单页应用
单页应用的路由模块的实现原理:
1、地址栏有锚值(有 # 号),刷新时浏览器发的请求不带 # 号及后面部分
2、通过location.hash 可以读写锚值(带#号)
3、改变锚值时会增加一条历史记录,但不会重载页面
4、window.onhashchange 监听锚值变化事件
5、另有history.pushState(状态对象, 新标题, 新url)方法和 window.onpopstate 事件。state与hash是两码事,pushState或回退的时候会触发onpopstate并传入状态对象
backbone自带路由模块,不能嵌套
angularjs 路由模块:https://github.com/angular-ui/ui-router ,可嵌套
reactjs路由模块:https://github.com/reactjs/react-router,可嵌套
组成部分:MV*、模块加载工具、路由、打包工具、UI组件
前端架构
1、无模块加载系统:html 引入一个js,js为一个自调用函数(闭包)
2、有模块加载系统:html 引入模块加载库 和 入口模块,入口模块中再去加载其他模块。这些模块可以是backbone的View、Angular的controller、React的component,也可以是完全自定义的闭包揭示模块、构造器模块
3、单页应用:在2的基础上监听 state 或 hash,重新渲染页面。
数据绑定
本质上是三种方式 1、(angularjs 1.x)监控数据变化,操作DOM;2、(react)整体更新虚拟DOM,diff,操作DOM;3、(vue)监控数据变化,局部更新虚拟DOM,diff,操作DOM
监控数据变化之脏检查
angularJS 1.x采用,在controller或$apply中改变scope的属性,会自动调用$dist,进行脏检查。发生变化的属性,调用其$watcher。view中写ng-,会自动为其创建$watcher。
监控数据变化之对象监控
Object.observe:Chrome、ES7;Object.observe(model,function(changes){ // changes里包含变化信息}),支持的浏览器少
监控数据变化之属性访问器
1、Object.defineProperty:Vue采用,原生支持
Object.defineProperty(obj,‘name’,//三个参数分别为:对象、属性名、属性描述对象
{
configurable: false, //属性是否可配置
enumerable: true, //属性是否可枚举
writable: true, //属性是否可重写,不能与set/get同存
value: null, //属性的默认值,不能与set/get同存
set: undefined, // 重写器,这里可以写入数据绑定逻辑
get: undefined // 读取器
})
Object.getOwnPropertyDescriptor(obj, 'name');// 获取属性描述对象
2、非原生:backbone的model,属性只能通过get 和 set访问,通过listen给他们挂钩子。backbone只给了监控数据变化的方法,不会自动操作DOM,隐藏没有数据绑定
监控数据变化之代理
用Proxy实现:Vue 3
MVVM对比
1、如何检查model变化:脏检查(angularjs) 、 访问器(vue、backbone)、整体重新渲染,无需检查(react)
2、如何更新视图:串行直接操作DOM(angularjs)、通过虚拟DOM
3、数据绑定:单项(react)还是双向(angularJS、Vue)
4、代码分离:JS、HTML是否分离,JSX需要经过编译,不好调试和定位错误
5、体积、社区、文档等
6、组件
Reactjs
demo: https://github.com/saoraozhe3hao/reactDemo
react
React为此引入了虚拟DOM(Virtual DOM)的机制:
在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,
然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新
而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并
Virtual DOM :维护在内存的类似于DOM树的JavaScript对象树,对变化批量更新至Web DOM树.虚拟DOM的节点记录了元素名称,属性名称和值
组件间的通信,父组件引用子组件,子组件回调父组件,EventEmitter(npm events 包)
在componentDidMount 里面可以元素 和 body、window 绑定原生事件,在componentWillUnmount里解除
JSX 转 JS
运行时转换:
打包时转换:
安装命令行工具 npm install -g react-tools
转换:jsx --watch jsx/ js/
Redux
Demo: https://github.com/saoraozhe3hao/reduxDemo
Flux
Flux是Facebook提出的一种 前端应用架构体系,其中 react 作为它的View层。
Redux
Redux 是 Flux 体系的一个实现框架
Redux实现单项数据流
import { createStore } from 'redux' // Redux 的API :createStore
import reducer from './reducer' // reducer需要自己实现,输入 state 和 action,返回新的state
const store = createStore(reducer) // 全局只创建一个 store,用于存放数据。可加第二个参数,作为store初始数据
// store的API:getState,获取store快照
store.subscribe(render) // store的API:subscribe,store变化时启动
事件 -> store.dispatch(action)[调用reducer,更新store] -> 触发store.subscribe注册的render或setState -> store.getState得到新的state
Redux 的API:combineReducers,用于对reducer的拆分
react-redux
import { Provider, connect } from 'react-redux' // react-redux 的 API:Provider、connect
const App = connect(
mapStateToProps, // function(state){return props}, 用于设置 UI组件的 props
mapDispatchToProps // function(dispatch){return {functions}} 用于给 UI组件的props 添加 事件处理函数,事件处理函数中调用 dispatch
)(UI 组件) // 返回一个容器组件
ReactDOM.render(
,
document.getElementById('root')
)
react-router-redux
import {browserHistory} from 'react-router'
import {createRouterMiddleware,synHistoryWithStore,push} from 'react-router-redux'
const store = createStore(
reducers,
applyMiddleware(createRouterMiddleware(browserHistory)) // 路由中间件
)
store.dispatch(push('/home')) //路由操作
中间件(middleware)redux-saga 改造store.dispatch实现异步分发action
import createSagaMiddleware from 'redux-saga' // redux-saga 的API:createSagaMiddleware
const sagaMiddleware = createSagaMiddleware() // 生成中间件
const store = createStore(
reducer,
Redux.applyMiddleware(sagaMiddleware) // Redux 的API:applyMiddleware,应用中间件
);
sagaMiddleware.run(saga) // saga 是一个Generator函数,含有action和处理函数的对应关系
saga
import { call, all,take, put,takeEvery } from 'redux-saga/effects'
export default function* saga() {
yield takeEvery('ACTION_TYPE', gen) // takeEvery 相当于 while(true){take},等待一个action,去执行gen
}
function* gen(){
var result = yield fetch(url); // fetch返回一个promise,该Generator函数调用者执行promise.then(function(data){g.next(data);}),g为gen返回的遍历器
console.log(result); // g.next后才会执行到这里,result被替代为g.next传进来的data
}
demo: https://github.com/saoraozhe3hao/webpackDemo
react的优点:组件式开发,高复用、低耦合
react生命周期
render: getDefaultProps、getInitialState、componentWillMount、render、componentDidMount
props change: componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate
state change:shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate
服务端渲染
首屏由服务端动态语言 根据数据和DOM结构 动态生成,以提高首屏渲染速度,同时利于SEO。
如果服务端用的 nodejs,浏览器端用的 reactjs ,那么可以在服务端用 ReactDOMServer.renderToString( APP ) 来生成渲染后的DOM结构,提供给浏览器。
相对于其他情况的 服务端渲染,nodejs + reactjs 这种情况不用单独去开发一套生成首屏的代码
构建
webpack 对 CommonJS 模块进行包装,使得能在浏览器运行
webpack 将 首屏所需的文件归纳成一个入口 chunk,并合并成bundle。非首屏所需文件,每个功能点所需的文件也归纳成一个个chunk,并合并成bundle,被按需加载。将代码拆分成不同的chunk,成为代码拆分 code splitting。
commonJS 模块按需加载方式
require.ensure(["module-a", "module-b"], function(require) {
var a = require("module-a"); // ensure只加载不执行,require的时候才执行
});
AMD模块按需加载方式
require(["module-a", "module-b"], function(a, b) {
});
ES6 模块按需加载方式(需要babel loader)
System.import('./module1.js')
.then(function(module1){
//use module1
}, function(e){
//handle error
});
import('./module2').then((module)=>{})
模块提取(剥离)
module.exports = {
entry: { //声明两个入口chunk
app: "./app.js",
app1: "./app1.js"
},
output: { // 所有入口chunk都将被打包入bundle.js。如果配置成"[name].js" ,将一个入口chunk打包成一个js
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin("vendor.bundle.js")
// 将所有入口chunk用到的公共模块提取出来(同时剔除),打包成vendor.bundle.js
]
};
demo: https://github.com/saoraozhe3hao/vueDemo
常用组件
路由:vue-router
状态管理:vuex
服务器端渲染SSR:nuxt
移动端组件库:Mint UI(饿了么)、vonic、vux (WeUI规范)、京东 NutUI、滴滴 CubeUI、有赞Vant、TDesign Vue(腾讯)
桌面端组件库:elememt UI(饿了么)、muse-ui(Material Design 规范)、iView(Ant Design 规范)、N3 Components、AT UI(京东)、Ant Design Vue(阿里)、TDesign Vue(腾讯)
专题:https://www.awesomes.cn/subject/vue
Material Design:谷歌提出的一套界面设计标准
element UI
安装:npm install element-ui --save
在main.js中引入:
import'element-ui/lib/theme-default/index.css'//引入element-ui的样式
import ElementUI from 'element-ui' // 引入 element-ui
安装使用:Vue.use(ElementUI) // 使用插件
VUX
更新nodejs版本
安装vux: npm install vux --save
安装vux-loader: npm install vux-loader --save-dev
在 webpack.base.conf.js 里配置 vux-loader。vux-loader使得组件能按需打包。
表单生产 vue-form-making
GitHub - GavinZhuLei/vue-form-making: A visual form designer/generator base on Vue.js, make form development simple and efficient.(基于Vue的可视化表单设计器,让表单开发简单而高效。)
http://docs.form.xiaoyaoji.cn/zh/
vue 引入工具库
import moment from 'moment';
Object.definePrototype(Vue.prototype, '$moment', { value: moment });
// 相当于 Vue.prototype.$moment = moment ;
vue实例中使用 this.$moment;
组件生命周期
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestroy、destroyed
选择vue的原因
学习成本低:国人出品,有很好的中文文档
开发效率高:angular和vue是双向绑定,react是单向绑定
运行性能高:vue和react都不用脏检查,并采用了虚拟DOM,angular需要脏检查
封装更简洁:组件要素(模板、脚本、样式)高聚合(可以写在一个文件里)低耦合(不混写)
开发更友好:react需要写JSX,angular需要写TypeScript,vue都是原生语法
实现更优雅:angular脏检查,react需要setState(),vue重写读写检查器
模板语法、computed、watch、methods的使用选择
1、简单关联逻辑,使用模板语法
2、多个属性的变化,都会影响一个属性,使用computed,减少watch
3、计算逻辑需要被重用,使用模板语法 调用 methods
4、一个属性的变化,要改变多个属性,选择使用watch 或 methods(绑定input事件)
5、不需要实时变化 或者 不需要每次变化都处理(例如重置),选择使用 methods(绑定click事件)
原理:history.pushState({}, 'title', 'path') 时,url会增加/path,但是页面不会刷新
1、Router 配置
new VueRouter({
mode:"history", // 使得 url 中不会出现 #
base:"/test" // 使得 路径前始终有 /test
}
2、webpack 配置
assetsPublicPath: '/test/' // 使得引用文件的路径始终有 /test/
3、nginx 配置
location /test {
try_files $uri $uri/ /index.html; // 使得跳到一个路由后 刷新浏览器,能把index.html返回回去
root /usr/test/;
}
MVVM框架的三个组成:数据监听器Observer、指令解析器Compile、订阅器Watcher
Observer:Dep是一个订阅发布模块,组件的一个 data 属性对应一个Dep实例;Vue扫描组件的 data ,用Object.defineProperty()给data的属性添加 getter / setter,在setter中触发Dep发布(Dep实例和getter / setter 在一个闭包中)
Watcher:一个组件实例对应一个Watcher实例,在Watcher中触发data属性的getter,在getter中添加Dep订阅
Compile:Compile是一个函数,用于解析模板指令,初始化渲染页面视图(将模板中的变量替换成数据),给每个指令对应的节点绑定更新函数,调用Watcher添加Dep订阅(数据有变动时,收到通知,触发更新函数,更新视图)
从视图交互变化到模型数据变化:解析模板指令时,对 v-model 的节点添加 input 事件处理函数,更新模型数据,进而触发 setter,从而触发Dep发布
监听数组变化:重写 [].__proto__ 里的方法,更改数组后,触发Dep发布
安装vue-cli:npm install -g @vue/cli@next
安装vue-cli-service:npm install -g @vue/cli-service-global
创建项目:vue create [project_name]
安装 axios、vue-router[4.0以上]、vuex[4.0以上]、less、less-loader(需要适配less的版本) 等依赖
页面配置:创建vue-cli配置文件 vue.config.js ,配置 pages
数据传递:
向下传:props、用$refs调用子组件的方法和变量
向上传:$emit、调用父组件传下来的props方法
任意传:利用 vuex
module.exports = {
devServer: {
proxy: {
'/api': { // 将前端发来的请求,转发到目标服务器,规避跨域问题(前端直接发请求到目标服务器)
target: 'http://192.168.0.2:8000', // 代理目标
ws: true, // 代理 websockets
secure: false, // 代理 https
changeOrigin: true // 跨域
}
}
},
pages: {
login: {
entry: 'src/login/main.js', // page 的入口
template: 'public/index.html', // html 模板
filename: 'login.html', // 在 dist里 的输出
title: '登录' // html 模板 里用到的变量
},
operation: {
entry: 'src/console/main.js',
template: 'public/index.html',
filename: 'index.html',
title: '控制台'
}
}
};
vue-cli-service
package.js中scripts下,vue-cli-service build --mode **,--mode指示的是使用哪个.env文件
.env文件中,NODE_ENV = production/development,指示构建方式
VueUse
Angular 4 + Webpack 4 Demo
https://github.com/saoraozhe3hao/angluar4Demo
npm install
npm install -g webpack-cli
npm run webpack-server
访问 http://localhost:8080/
Angular 与 AngularJS
AngularJS = AngularJS 1.x
Angular = Angular 2+ ,Angluar4兼容Angular2
AngularJS中子组件scope会继承父组件scope的字段,子组件中可以改变父组件的字段,脏检查时会出现循环往复。Angular2 编译器为每个组件自动创建各自的变化检查器,每次检查都是单向的。
Angular使用Zone.js包裹了原生的setTimeout,addEventListener、promise等异步操作,并setup了相应的钩子。异步回调时触发相应钩子,通知angular2做相应的脏检查处理,不用$scope.$apply()。
Angular 4
组件初始化生命周期
constructor -> ngOnChanges -> ngOnInit -> ngDoCheck -> ngAfterContentInit -> ngAfterContentChecked -> ngAfterViewInit -> ngAfterViewChecked -> ngOnDestroy
组件更新生命周期
ngOnChanges -> ngDoCheck -> ngAfterContentChecked -> ngAfterViewChecked
框架模块
@angular/core @angular/common @angular/forms @angular/http @angular/router
项目结构
index.html -> main.ts(入口文件) -> app.module.ts(主模块,应用模块) -> app.component.ts(根模块) -> 特性模块 -> 共享模块