不用脚手架自行搭建Vue项目
历史
- Vue1.0版本时确实是MVVM 但是后来就在逐步向MVC架构发展的了
基本概念与规范
let vm=new Vue();vm叫Vue实例vue单文件叫组件, 其实是一个对象,不是Vue实例- 导入的组件名用大写开头,文件名则最好小写:
import Demo from './demo.vue' - 使用data中的数据
this.n, 等同于this.$data.n而不是this.data.n - this.set方法
- el属性就是this.$mount('...') --- 设置挂载点
- const vm=new Vue( options ) // options 被称为构造选项
- 不要在options中使用箭头函数,原因如下:
- vue中频繁用到this,但options中的箭头函数中的this -> window (new Vue是函数调用而非定义)
- 为什么options中普通函数的this -> Vue实例vm呢? 因为Vue源码保证了
- 无论是
v-on之类的官方指令还是自定义指令,都是用于封装dom操作- 比如
v-on,Vue底层会替你调用addEventListner把元素和函数绑定好,这当然就是dom操作
- 比如
methods,computed,watch都是对象的形式,因为它们各自有多个items- {{}}
插值表达式, 仅支持单行js表达式new Vue({ watch:{ n:()=>{ // 箭头函数中不存在this,this来自于外部 // 你想要this -> Vue实例vm,这是在意淫 // 这里箭头函数中的 this -> window, 因为new Vue()是函数调用而非函数定义 // this借用的是外部this,这里的外部就是全局window了 } } }) - 没必要用 Vue的
filter过滤器,可以用methods中写一个filter函数去替代
Vue书写中的细节
vue create demo-1 初始配置
Vue的html代码是如何被浏览器解析的?
我们知道传统的html代码其实就是字符串,
这些字符串在做了一些声明之后就可以被浏览器读到的时候给转成dom节点;
Vue中的html代码(字符串)挺特殊的,
比如混有v-if,v-for,{{n}}这些在其中,那到底浏览器是如何转成dom节点的呢?
原来vue的完整版中有着一个编译器,
会把混有v-if,v-for,{{n}}这些的html代码翻译成Vue中内置用以生成dom节点的函数,名叫h函数
但是编译器的体积太大了,所以作者其实是不推荐使用带有编译器的完整版Vue的,
比如cli脚手架工具其实就是默认使用运行时版本的Vue,
那么运行时版本的Vue没有了编译器是如何让浏览器解析混有v-if,v-for,{{n}}这些的html代码的呢?
答案是通过webapack中的vue-loader这个加载器,
vue-loader的作用和编译器一样其实就是把混有v-if,v-for,{{n}}这些的html代码
调用Vue中内置用以生成dom节点的h函数,重构整个代码
证明cli中的 vue-loader 会帮我们自动转译.vue中的html==>js
完整版Vue VS 运行时版本Vue
- 完整版 运行时+编译器
- 非完整版即运行时版 只包括运行时,不包括编译器
通过vue-cli默认安装的就是运行时版本的Vue
完整版Vue如何书写视图层
完整版Vue书写视图层的方式可以有三种
- html格式的文件中比如src/index.html (下图1+图2)
- Vue实例或者Vue组件中的template属性里书写html代码 (下图3)
- 当然也可以在.vue单文件中< template >标签中进行书写-----只不过这有点显得大材小用
mvc架构的视图通常是写在V这个对象中的,而不是直接从html代码中获取的,但这里却可以,是因为这对小白更友好
![]()
![]()
非完整版Vue如何书写视图层
非完整版Vue不支持在.html文件中书写html也不支持在Vue实例或者Vue组件的template属性中书写html
- 可以在Vue实例或者Vue组件的render方法中调用h函数以手动生成dom节点的方式书写----缺点是太复杂
- 可以在.vue结尾的单文件中书写html----这是最佳方法
template属性 VS render方法 (在Vue组件或者Vue实例中的)
下图源自官网
compiler VS vue-loader
- compiler存在于完整版Vue源码中,其功能是把vue的
html代码 其实就是字符串(注意,这些字符串很复合比如有v-if,{{n}}这些) 转化成dom节点 - vue-loader则属于webpack的内容,是vue-cli默认的配置项,作用和编译器是一样的,将复合的
html字符串用生涩的h函数重新渲染成js文件
如何使用Vue实例?
按理说是三种方式 ,但实际只用单文件方式这一种
方式1 直接在index.html中书写html代码或者在Vue实例中的 template属性中书写html代码(需要完整版本Vue); // 缺点是完整版本Vue源码中多了编译器,因此体积大了百分之四十
方式2 在Vue实例中书写render方法配合h函数一同构造视图(运行时版本Vue) // 虽然体积节约很多,但是缺点是这种方式生涩难写,因为这等于是用vue封装好的h方法直接生成并操作dom元素
方式3 单文件.vue文件方式,依旧允许以传统方式去书写html(运行时版本Vue) // 体积节约很多,还需要有webpack的vue-loader加载器去配合, // 因为你写的传统html代码(字符串)需要被vue-loader转化成h函数那种操作dom的方式,就可以生成dom节点了。
对SEO的影响
由上可见,传到浏览器的大多都是由vue-loader调用h函数渲染成的js代码或者说字符串,
然后浏览器是通过解析js来渲染页面的
现在问题就在于,js是不能被搜索引擎所理解的,因此SEO往往不理想
优化思路
- 可以给index.html中的app节点上写上一些对SEO有帮助的内容
- title description keyword h1 a 这些标签写好即可
这样一来也就能让搜索引擎在curl访问你的网页的时候能提取适当的信息
不过由于国内的SEO本身也就是以收费为主,所以不必太较真。
详解Vue实例
const vm=new Vue( options )
// options 被称为选项或者构造选项
// vm 是尤雨溪的惯用,即Vue实例,该实例封装了数据读写,事件绑定,DOM更新,但不包含ajax
options
- 数据
- DOM
- 生命周期钩子
- 资源
- 组合
面试题:data为什么是一个函数?(Vue全解-class2-4th)
使用组件的三种方式
引入使用组件的方式有三种,
1.单开一个.vue文件然后import这叫单文件组件,在需要用的地方先import再声明才能用
2.Vue.component(id,options) 这叫全局组件,随处声明,随处使用
3. 在Vue实例内部定义一个组件
值得注意的是,[全局组件]或者[在Vue实例中components属性上定义的组件]里面的属性都与Vue实例中的属性一模一样
生命周期钩子 $nextTick
props 用于父->子传值
当更新了
data,立马获取视图中的数据的话,并不会得到你想要的结果。
要么你在原地使用nextTick,要么你就在updated里去操作
在线示例
props在vue中指的就是用于传参的属性,既可以是
propName也可以是:propName
:propName=“xxx” 里面的xxx写的是js代码,甚至可以传递函数
- 下图是在父组件中,在往子组件中传值
在线示例
.prop修饰符 property attribute
先上结论:
.prop修饰符的作用其实就是把vue中的prop转化成像一个真正的property一样挂在于最外层上便于读取 .prop修饰符的在线示例
- 写在子组件身上的
label都是Vue中的prop, 都是可以在子组件内读取的 - 但类似
style这样的保留的prop是无法被子组件读取的 - 因此以下这些都是vue的
prop
什么是js中的
property? 以下这些属性都是property,存在于dom结构最外层, 可以用点直接访问什么是js中的
attribute? 存在于一个property叫attributes中的所有属性都是attributegetAttribute是原生js中的api经测试,
title,id,style,既是property又是attribute,也就是说他们既挂在最外层,又存在于attributes这个对象中
vue中自定义的
propName存储于哪里,目前并不清楚,即不挂在最外层也不存在于attributes这个对象中
Vue数据响应式原理
set方法
在方法中我们只可通过
this.n或者this.obj.a类似这样的去操作已存在的数据,但如果要创建尚未存在的次根级别数据则需要用到set,Vue不支持操作根级别数据set方法不要去操作数组,数组用变异方法
Vue.set===this.$set===vm.$set两种set方法一模一样,仅仅为了避免与你自己写的set方法重名- set方法只可设置data对象第二层及其往后的数据
- set原理就是调用Object.defineProperty,但参数不同,举个例子
- this.$set(this.obj,'b',1)
图示,举个例子
this.$set(this, 'b', 1); // 非法
this.$set(this.obj, 'b', 100); // 合法
下面这张图来自于Vue官网,同样的理解
为什么无法set data中的第一层属性?
请看Vue数据响应式的源码的完整思路!中,Vue对 data 对象中属性的监听思路是,会对 data 的属性调用 Object.defineProperty ,但如果你最初连第一层属性都没给,Vue是不会去执行数据响应式监听的
Vue中数组的七种变异方法
Vue是如何制造数组的变异方法的? 拿push举例,原理类似下面
class VueArray extends Array{
push(...args){ // push方法挂在VueArray.prototype上
const oldLength = this.length // this 就是arr
super.push(...args) // super就是调用父类的push
console.log('push ')
for(let i = oldLength; i<this.length; i++){
// 对新增项调用set方法让Vue监听到
Vue.set(this, i, this[i])
}
}
}
const arr=new VueArray(1,2,3,4);
console.log(arr)
Computed 计算属性
Watch 侦察属性
面试题: Computed, Watch, Methods 区别
v-bind :class :style 数组语法 对象语法
总结: 主要还是推荐使用对象语法,
:class="{ active: isActive}"或者直接:class="{active}"都可以,只不过后面这种是省略形式的写法,依旧需要在data中定义
v-model .sync修饰符 数据双向绑定
模板与指令
xml VS html
Vue的template中运用的是xml而不是html
- xml中标签必须闭合比如
<input name="username"/> - html则不必比如
<input name="username"/ >与<input name="username">都算对 - xml可以允许
<div/>表示无内容的div标签,而html则不可以允许这种写法出现 | | xml | html | |------|------------|------------| |<input name="username"/>| yes | yes | |<input name="username">| no | yes | |<div/>表示无内容的div | yes | no |
模板中的胡子语法支持js运算但不支持if else
- v-html会对内容进行编译
- v-pre不会对内容进行编译
- v-bind
- v-on
- v-if
- v-show
- v-cloak
v-cloak用于解决网络不畅时页面显示过多的
{{ }}问题。
当网络阻塞时,vuejs还不能够及时加载进来,页面中会出现很多的插值表达式,v-cloak的作用是在阻塞时生效一个样式,当恢复时自动把样式移除
通常与display:none 一同使用
指令修饰符
@click.stop // 阻止事件冒泡
@click.prevent
@click.stop.prevent // 同时表示两种意思
@keypress.keyCode // 比如@keypress.13表示回车
这里提一下input,change,keypress事件
- input事件大体是指在input输入框之类的进行文本的值的修改时触发(触发频率非常高),
你敲打回车是不会触发input事件的,因为没有值的修改 - change事件大体是指在失焦或者提交时触发,(触发频率不高)
- keypress事件大体是指按下键盘时触发(触发频率很高),
想识别敲下的是回车键就用keypress
自定义指令
mixins作用是重复利用组件间相同的options
Provide和Inject
什么是Provide Inject? Provide Inject是一种依赖注入模式,实现了祖先组件与后代组件之间的通信,祖先组件中provide一些data和method,在需要使用这些东西的子组件中inject即可
点击查看在线示例provide inject的特性是
数据不可响应的,意思是说通过祖先组件provide出去的data一旦改变,那些注入了data到自身组件中使用的后代组件们是无法得到更新的
Vue路由
前端框架中指的路由就是当你输入不同url时,页面会展示不同的内容
- 路由三种模式: hash,history,memory
- hash就是# window.location.hash,服务器是收不到#后面的内容的,因此seo不友好
window.location.hash.substr(1)hashchange事件 - history
window.location.pathname - memory就是利用了localstorage去存储,但是无法分享了因为是本地的
Vue数据传递的例子
is属性 切换子组件
slot 插槽
- 基础插槽,具名插槽,作用域插槽
- 作用域插槽主要用于封装组件 父组件和子组件可以各司其职
- 写作用域插槽时当然不能直接在子组件内直接写
<slot v-for="item in arr">{{ item }}</slot> - 因为这里面的内容是插槽的默认内容,只要你父组件传递了东西,里面就会被覆盖掉。
子组件如何访问插槽中传来的的内容this.$slots.slotName得到一个数组 在线示例 在线示例