1. 响应性
工作原理:
- 使用ES5的Object.definePropery,重写啦所有属性的getter和setter方法
- 实际上就是依赖跟踪的基本形式
简易getter和setter
const obj = { foo: 123}
convert(obj)
function convert(obj) {
Object.keys(obj).forEach(key =>{
let internalValue = obj[key]
Object.defineProperty(obj,key,{
get(){
return internalValue
}
set(newValue){
internalValue = newValue
}
})
})
}
依赖收集
window.Dep = class Dep{
constructor(){
this.subscribers = new Set()
}
depend(){
if(activeUpdate){
this.subscribers.add(activeUpdate)
}
}
notify(){
this.subscribers.forEach(sub =>{
sub()
})
}
}
let activeUpdate
function autorun (update){
function wrappedUpdate(){
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
}
const dep = new Dep()
autorun(()=>{
dep.depend(){
console.log('updated')
}
})
dep.notify()
迷你观察者
//依赖收集
class Dep(){
constructor(){
this.subscribers = new Set()
}
depend(){
if(updatevalue){
this.subscribers.add(updatevalue)
}
}
notify(){
this.subscribers.forEach(sub => {
sub()
})
}
}
let updatevalue
// 订阅信息
function autorun(update){
function wrappedUpdate(){
updatevalue = wrappedUpdate
update()
updatevalue = null
}
}
//新建依赖
let dep = new Dep()
//监听变化
function observe(obj) {
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj,key,{
get(){
return value
}
set(newValue){
value = newValue
dep.notify()
}
})
})
}
const state = {
count : 0
}
observe(state)
autorun(()=>{
dep.depend(()={
console.log('update')
//渲染
})
})
state.count ++
2. 插件
- vue.mixin
const myPlugin = >{
install(Vue){
Vue.mixin({
created(){
if(this.$options.rules){
Object.keys(this.$options.rules).forEach(key=>{
const rule = this.$options.rules[key]
this.$watch[key,(newValue)=>{
const result = rule.validate(newValue)
if(!result){
console.log(rule.message)
}
}]
})
}
}
})
}
}
Vue.use(myPlugin)
const vm = new Vue({
data: {foo: 10},
rules:{
validate: value => value > 1,
message: 'foo must be greater than one'
}
})
3. 渲染
- 修改原生dom复杂,产生啦虚拟dom
- 虚拟dom并不会更快,只是原生dom的局限性的一个方法
- 它提供啦以声明方式构成你想要的dom的结构
- 渲染逻辑从真实的dom中分离出来半截取消对真实dom来说等于没有执行
jsx和template区别 - tempalte更静态有约束力,jsx更动态
- 渲染api h(‘div’,{class: ‘foo’},‘some text’)
export default {
render (h) {
return h('div',{},[...])
}
}
- 动态渲染标签组件
<div id="app">
<foo>
<example :tag="['h1','h2','h3']">
<div>foo</div>
</example>
</foo>
</div>
<script>
Vue.Component('example', {
props: ['tags'],
functional: true,
render(h,{props: {tags}}) {
return h('div',context.props.tags.map((tag,i) => h(tag,i)))
}
})
new Vue({el:'#app'})
</script>
<div id="app">
<example :ok="ok"></example>
<button @click="ok = !ok">toggle</button>
</div>
<script>
const Foo = {
render: h => h('div','foo')
}
const Bar = {
render: h => h('div','bar')
}
Vue.component('example', {
props: ['ok'],
render(h) {
return this.ok ? h(Foo) : h(Bar)
}
})
new Vue({
el: '#app',
data: {ok: true}
})
</script>
3. 高阶组件
- 高阶组件对简单组件进行扩展,增强组件功能又不改变组件原来的功能
<div id="#app">
<smart-avatar username="vuejs" id="foo"></smart-avatar>
</app>
function fetchURL (username,cb){
setTimeout(()=>{
cb('https:avatar.githubusercontent.com/u/68342343?v=4&s=200')
},500)
}
const Avatar = {
props: ['src'],
template: '<img :src = "src">'
}
function withAvatarURL(InnerComponent,fetchURL){
return {
props: ['username'],
data(){
url: 'http://via.placeholder.com/200*200'
},
created(){
fetchUrl(this.username,url=> {
this.url = url
})
},
render(h) {
return h(InnerComponent,{
props: {
src: this.url,
attrs: this.$attrs
}
},this.$slots.default)
}
}
}
const SmartAvatar = withAvatarURL(Avatar)
new Vue({
el: '#app',
components: {SmartAvatar}
})
4. 状态管理
- 父组件传子组件用props
- props有限制,当父组件需要传递的值改变时,props传到子组件的值并不会改变
- 共享对象
const state = {
count: 0
}
const Counter = {
data(){
return state
},
render(h){
return h('div',this.count)
}
}
new Vue({
el: '#app',
components: {Counter},
methods: {
inc() {
this.count++
}
}
})
const state = new Vue({
data: {
count: 0
},
methods: {
inc() {
this.count++
}
}
})
const Counter = {
render: h => h('div',state.count)
}
new vue({
el:'#app',
components: {
Counter
},
methods: {
inc(){
state.inc()
}
}
})
- mutations
function createStore ({state,mutations}) {
return new Vue({
data: { state },
methods: {
commit(mutationType){
mutations[mutationType](this.state)
}
}
})
}
const store = createStore({
state: {count: 0},
mutations: {
inc (state){
state.count++
}
}
})
const Counter = {
render (h) {
return h('div',store.state.count)
}
}
new Vue({
el:'#app',
components: { Counter},
methods: {
inc() {
store.commit('inc')
}
}
})
5.路由
- 我们需要在应用中将URL保存为响应式状态,为啦我们应用能够响应它的变化
<div id="app">
<component :is="url"></component>
<a href="#foo">foo</a>
<a href="#bar">bar</a>
</app>
window.location.hash
window.addEventListener('hashchange',() => {
app.url = window.location.hash.slice(1)
})
const app = new Vue({
el: '#app',
data: {
url: window.location.hash.slice(1)
},
components: {
foo: {template: '<div>foo</div>'},
bar: {template: '<div>bar</div>'}
}
})
路由表
const Bar = {template: '<div>bar</div>'}
const NotFound = {template: '<div>not Found!</div>'}
const routeTable = {
'foo': Foo,
'bar': Bar
}
window.addEventListener('hashchange',()=>{
app.url = window.location.hash.slice(1)
})
const app = new Vue({
el: '#app',
data: {
url: window.location.hash.slice(1)
},
render (h) {
return h('div',{
h(routeTable(this.url) || NotFound),
h('a',{attrs:{href: '#foo'}}, 'foo'),
h('a',{attrs:{href: '#bar'}}, 'bar'),
})
}
})
正则表达式
- 动态路径通过正则表达式实现
- 我们需要一个转换器去利用正则表达式转换
- 这可以帮助我们在运行时进行解析
- path-to-regexp 这个库就是一个转换器
'/user/:username'
'/user/123?foo=bar'
{
path: '/user/123',
params: { username: '123' },
query: { foo:'bar' }
}
// keys 填充动态部分
var keys = []
var re = pathToRegexp('/foo/:bar',key)
// re = /^\foo\/([^\/] + ?)\/?$/i
// keys = [{name:'bar',prefix:'/',delimiter:'/',optional: false,repeat: false}]
const keys = []
const regex = pathToRegex('foo/:bar',keys)
const result = regex.exec('/foo/123')
// ['123']
// [{name; 'bar'}]
// {bar:'123'}
动态路由
const Foo = {
props: ['id'],
template: `<div>foo with id: {{id}}</div>`
}
// /foo/123
const Bar = {template: `<div>bar</div>`}
cosnt NotFound= {template: `<div>not found!</div>`}
const routeTable = {
'foo/:id': Foo,
'bar': bar
}
window.addEventListener('hashchange',()=>{
})
let compiledRoutes = []
Object.keys(routeTable).forEach(key => {
// 正则匹配
var keys = []
var re = pathToRegexp(path,key)
const component = routerTable[path]
compiledRoutes.push({
component,
re,
key
})
})
compiledRoutes.some(route => {
const match = route.re.exec(path)
if(match){
componentToRender = route.component
route.key.resuce((segment,index)=>{
props[segment.name] = match[index + 1]
})
}
})