MVVM
Model(json数据)-View(html视图)-ViewModel(用来关联前两者,实现数据和视图的双向绑定)
用vue举例:
Model就是其中的生命周期、data、methods等有关数据操作的内容
View就是template中的html属性部分,即页面展示的样子
ViewModel就是template中和数据有关联的部分,如v-bind动态绑定Model中数据、v-for循环Model中的数据,等这些操作的部分。
优点:
可复用性:把一些数据逻辑放到一个ViewModel 中,让很多 view 来重用。比如vue中的组件可以被复用。
低耦合:view和model没有直接联系在一起
vue目录结构
├── build/ # Webpack 配置目录
├── dist/ # build 生成的生产环境下的项目
├── config/ # Vue基本配置文件,可以设置监听端口,打包输出等
├── node_modules/ # 依赖包,通常执行npm i会生成
├── src/ # 源码目录(开发的项目文件都在此文件中写)
│ ├── assets/ # 放置需要经由 Webpack 处理的静态文件,通常为样式类文件,如css,sass以及一些外部的js
│ ├── components/ # 公共组件
│ ├── filters/ # 过滤器
│ ├── store/ # 状态管理
│ ├── routes/ # 路由,此处配置项目路由
│ ├── services/ # 服务(统一管理 XHR 请求)
│ ├── utils/ # 工具类
│ ├── views/ # 路由页面组件
│ ├── App.vue # 根组件
│ ├── main.js # 入口文件
├── index.html # 主页,打开网页后最先访问的页面
├── static/ # 放置无需经由 Webpack 处理的静态文件,通常放置图片类资源
├── .babelrc # Babel 转码配置
├── .editorconfig # 代码格式
├── .eslintignore # (配置)ESLint 检查中需忽略的文件(夹)
├── .eslintrc # ESLint 配置
├── .gitignore # (配置)在上传中需被 Git 忽略的文件(夹)
├── package.json # 本项目的配置信息,启动方式
├── package-lock.json # 记录当前状态下实际安装的各个npm package的具体来源和版本号
├── README.md # 项目说明(很重要,便于其他人看懂)
指令:带有 v-
前缀的特殊 attribute。
*动态参数
1.可以用方括号括起来的 JavaScript 表达式作为一个指令的参数
<a v-bind:[attributeName]="url"> ... </a>
2.空格和引号,放在 HTML attribute 名里是无效的。变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
<!-- 这会触发一个编译警告 --> <a v-bind:['foo' + bar]="value"> ... </a>
3.DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写
4.缩写:v-bind为“:”,v-on为“@”
vue框架的watch:
使用watch监听数据时,有三个选项,handler,deep,immediate
2.1vue使用watch来响应数据的变化。
2.2当值第一次绑定时,不会执行监听函数,只有值发生改变才会执行。
2.3 immediate属性:如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true。immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
2.4 deep属性:
设置deep: true 则可以监听到cityName.name的变化,此时会给cityName的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性。
vue框架中数据渲染该放在created阶段还是mounted阶段:
3.1 理解:
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。($el还未被创建)
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。(el属性创建并挂载完成)
如果在mounted钩子函数中请求数据可能导致页面闪屏问题
其实就是加载时机问题,放在created里会比mounted触发早一点,如果在页面挂载完之前请求完成的话就不会看到闪屏了。
计算属性和方法
计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。如果你不希望有缓存,请用方法来替代。
父子组件之间的通信
路由
1*过程清理:
①先npm install vue-router 安装路由插件,此时配置文件中的依赖项会加多一个router属性。
②紧接着创建一个放路由的文件router进行路由配置(需导入依赖项)。
③再来到根组件App.vue中将路由跳转(router-link)和路由显示(router-view)配置出来
④再在入口文件main.js中引入该依赖项(记得要放入实例中)。
⑤当有子路由时,需要在父路由跳转到的组件中放置子路由相关的路由跳转(router-link)和路由显示(router-view)【也可以使用push方法实现跳转】(也就是根组件中点击了父路由的router-link内容,根组件router-view处会显示子路由的router-link内容,再点击子路由的router-link内容,父路由的router-view处会显示子路由页面内容)
2*访问路由的两种方式:
标签导航<router-link><router-link>是通过转义为<a></a>标签进行跳转,其中router-link标签中的to属性会被转义为a标签中的href属性;
编程式导航:我们可以通过this.$router.push()这个方法来实现编程式导航,当然也可以实现参数传递,这种编程式导航一般是用于按钮点击之后跳转。*this.$router.push('home') push方法会向 history 栈添加一个新的记录
3*path:'name'和path:'/name'的区别:
如果加/则会被当作根目录,否则当前的路径会嵌套在之前的路径中。
4*动态路由:
通过获取路径显示的内容并放到页面上,就可以将路由变成动态的,即/后面加需要用到的数据。
route:首先它是个单数,译为路由,即我们可以理解为单个路由或者某一个路由;
routes:它是个复数,表示多个的集合才能为复数;即我们可以理解为多个路由的集合,JS中表示多种不同状态的集合的形式只有数组和对象两种,事实上官方定义routes是一个数组;所以我们记住了,routes表示多个数组的集合;
router:译为路由器,上面都是路由,这个是路由器,我们可以理解为一个容器包含上述两个或者说它是一个管理者,负责管理上述两个;举个常见的场景的例子:当用户在页面上点击按钮的时候,这个时候router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由;
route:$route对象表示当前的路由信息(当前激活的路由对象),包含了当前 URL 解析得到的信息。包含当前的路径,参数,query对象等
1.路由元信息meta:
*每个路由都有一个meta元数据字段用来自定义信息,供页面组件或路由钩子函数使用
*$route.meta.*** 获取meta数据
*window.document.title = to.meta.*** 可改变网页标题,用户登录鉴权
2.路由中导航钩子中next()方法:进行管道中的下一个钩子
3.$route.path:当前路由路径,解析为绝对路径eg. /foo/bar
4.$route.params:路径/foo?user=1,则有 $route.query.user == 1
5.$route.matched:数组对象,包含当前路由的所有嵌套路径片段的路由记录(routes 配置数组中的对象副本 (还有在 children 数组))
6.$route.query:
7.$route.fullPath:解析完后的url
router:$router对象是全局路由的实例,是router构造方法的实例。
*this.$router.go(-1)
*this.$router.replace('/') replace方法是替换当前的页面,
路由权限
**1 导航守卫:
用来判断页面是否需要跳转,需要在router中的index.js文件夹下使用router.beforeEach((to,from,next) => {.....})
/*
“to”: 即将要进入的目标 路由对象;(这个对象中包含name,params,meta等属性)
"from": 当前导航正要离开的路由对象;(这个对象中包含name,params,meta等属性)
“next”: Function: 确保要调用 next 方法,否则钩子就不会被 resolved。这个当中还可以传一些参数,具体可以看官方文档。
*/
**2 routes{}中的meta属性:用于进行权限控制,不同用户的访问权限不同:
如:当点击进入/blog页面时,如果不符合条件则跳转到/login
vuex中的modules
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块,
**store文件结构:
**index.js中动态引入modules:
const modulesFiles = require.context('./modules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters
})
export default store
**简单的引入:
import Vue from 'vue'
import Vuex from 'vuex'
import bus from './module/app'
import app from './module/errorLog'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 这里是根vuex状态
},
mutations: {
// 这里是根vuex状态
},
actions: {
// 这里是根vuex状态
},
modules: { // 子vuex状态模块注册
namespaced: true, // 为了解决不同模块命名冲突的问题
app,
errorLog
}
})
**modules中的errorLog.js文件规范:
const state = {
logs: []
}
const mutations = {
ADD_ERROR_LOG: (state, log) => {
state.logs.push(log)
},
CLEAR_ERROR_LOG: (state) => {
state.logs.splice(0)
}
}
const actions = {
addErrorLog({ commit }, log) {
commit('ADD_ERROR_LOG', log)
},
clearErrorLog({ commit }) {
commit('CLEAR_ERROR_LOG')
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
**工具类utils.js中使用:
// 引入 这里的store 就相当于页面中的 this.$store
import store from '@/store'
export const setCurUser = (user) => {
let curUser = store.app.user
if(!curUser) {
store.commit("app/setUser", user) /*文件名/函数名*/
return user
}
return curUser
}
vuex中的actions
作用域插槽和自定义事件有什么区别呢?
slot、作用域插槽,你真的懂了吗?_sunhonghui9527的博客-CSDN博客_slot作用域插槽
vue中组件的data为什么必须是一个函数而不能是一个对象实例?
首先需要理解对象实际存储的是数据存储的地址值,任何地方修改该对象中的属性,实际修改的是同一份数据的值。
组件最终会被多个页面复用,也就是说,如果data保存的是一个地址值(一个对象实例),那么所有复用该组件的页面使用的都是同一份数据的值(这份数据变成公有的),一旦一个调用该组件的地方修改了该值,也就是修改了公有数据,所有调用该组件的地方数据都会改变。
而用函数来存储数据,使它return一个对象实例,也就是每个复用该组件的地方使用的数据实际是函数中新创建并return的对象(开辟了不同的存储空间,指向不同的地址值)。
待更新