脚手架搭建:
# install vue-cli
$ npm install -g vue-cli //全局安装,将vue变成一个命令行中的命令
# create a new project using the "webpack" boilerplate
$ vue init webpack my-project
# install dependencies and go!
$ cd my-project
$ cnpm install //每次你去下载一整个的应用都要去下载相对应的依赖,每个应用应该有几十个依赖,这些依赖的信息在package.json中都有说明,直接npm install 就能直接将所有需要的依赖安装,我已经把npm 设置成了淘宝的镜像
$ npm run dev// 这样就运行起来了,其实这里的dev这些设置也是 已经提前在package.json里面设置好了,要注意在你的项目路径下面运行,不然会找不到package.json文件的。
在这里先抛出一个问题,为什么在命令行输入npm run dev就可以运行了?
解析vue-cli中的页面结构
为什么需要vue-cli?
因为如果是大型项目的话,我们需要项目部署,构建,热加载等,利用脚手架可以快速构建项目应用。
可能第一次用vue-cli看到下面这么多的文件有点多有点复杂,但是别怕,咱们来一一了解
|-- build // 项目构建(webpack)相关代码
| |-- build.js // 生产环境构建代码
| |-- check-version.js // 检查node、npm等版本
| |-- utils.js // 构建工具相关
| |-- vue-loader.conf.js // webpack loader配置
| |-- webpack.base.conf.js // webpack基础配置
| |-- webpack.dev.conf.js // webpack开发环境配置,构建开发本地服务器
| |-- webpack.prod.conf.js // webpack生产环境配置
|-- config // 项目开发环境配置
| |-- dev.env.js // 开发环境变量
| |-- index.js // 项目一些配置变量
| |-- prod.env.js // 生产环境变量
| |-- test.env.js // 测试脚本的配置
|-- src // 源码目录
| |-- components // vue所有组件
| |-- router // vue的路由管理
| |-- App.vue // 页面入口文件
| |-- main.js // 程序入口文件,加载各种公共组件
|-- static // 静态文件,比如一些图片,json数据等
|-- test // 测试文件
| |-- e2e // e2e 测试
| |-- unit // 单元测试
|-- .babelrc // ES6语法编译配置
|-- .editorconfig // 定义代码格式
|-- .eslintignore // eslint检测代码忽略的文件(夹)
|-- .eslintrc.js // 定义eslint的plugins,extends,rules
|-- .gitignore // git上传需要忽略的文件格式
|-- .postcsssrc // postcss配置文件
|-- README.md // 项目说明,markdown文档
|-- index.html // 访问的页面
|-- package.json // 项目基本信息,包依赖信息等
- main.js 程序入口文件,加载各种公共组件
第一部分(import)导入要用到的组件
第二部分新建了一个Vue对象,组件里面写的是App,也就是说导入 App.vue,在入口的时候将app的东西都显示出来。
-
看看App.vue里面写的是什么东西
这里引入了一个组件 componentsA,直接以标签的形式加在html里面。
要在下面的export default里面的components进行注册,不然是用不了的。
对于组件的理解:组件是可以复用的Vue实例,带有名字,也可以说是一个自定义元素。是页面的一部分,就是可能其他地方也要用到,所以就封装起来,所以要用的时候进行导入。 -
关于路由
我们看一下router这个东西里面写了什么
设置了Helloworld这个组件的path为/
要注意,这个路由的出口就是在html中的<router-view></router-view>
helloworld这个组件最后显示的应该就是‘welcome to your Vue,js App'
我们在App.vue里面的html的最下面定义了路由出口
看看网页
成功
router对于构建大型单页应用还是有很大优势的,嵌套路由,视图切换动画,具名路径。
其他
-
每个Vue应用都是用Vue函数创建一个新的实例开始的
var vm = new Vue({ //选项 })
在我的理解,Vue就是一个构造函数,所以我们这个实例化的对象vm也继承了Vue的特点。
-
在App.vue中
new Vue({ el: '#app', data: { message:'hello Vue.js' } })
el指定挂载的位置,挂载到id为app的元素上
- 响应式系统中加入了所有data对象中所含有的属性,也就是当这些属性的值发生改变时,视图中这些属性的值也会相应的改变。这也是一个很重要的特点。
来一个最最简单的例子
<p div = "app">库存:{{a}}</p>//html
var vm = new Vue({
el:"#app",
data:{
a:100
}
method:{
function(){ }
}
}) //script
data代表vue对象的数据,data这里声明,数据要在method代表vue对象的方法,所以function要在methods这里声明。
可以看到<p></p>之间的{{a}}就是用了我们下面定义的id为“app"的vm这个对象里面的data对象的a属性的值。
在控制台输入 vm.a = 190 回车之后我们看到
这里的库存也改变了,这也是Vue的一个特点:响应式(relative)
可以想象接入后端数据库之后处理也是特别方便的。
- 除了数据属性,还有一些实例属性和方法,他们都有前缀$,以便与用户定义的属性区分开来。
相关api参考
https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E5%B1%9E%E6%80%A7
为什么组件中的data一定要用reurn 返回
data(){
return {}
}
- 为什么要做成组件,因为可以多次使用,比较方便。
- 如果多个地方同时用到一个组件,一旦其中一个地方的data改变,用return返回的话就不会影响到其他组件中data的值,因为不是引用值(引用值就是不用return 直接 f:1,d:2这样,而且数据只有一份,一旦更改,其他地方的这个组件也会更改)
数组方法中的concat(),slice(),filter(),直接复制,或者改变数组长度的这些方法不会触发视图的更新。
一些 Vue独有的函数及其用法:
v-if
1.1 基本用法
<div id="app_2">
<p v-if="seen">你现在可以看到我</p>
</div> //html
var app2 = new Vue({
el:"#app_2",
data:{
seen:true
}
}) //script
v-if是类似于一个if的函数,进行判断并操作,也就是说如果seen这个属性是true的话,就会显示文字,如果这个属性是false的话,就不会显示文字,所以如果你在控制台输入app2.seen=false的话,"你现在可以看到我"这行文字是不会出现的。
1.2 <template> 元素
因为v-if是一个指令,所以必须绑定在一个上,如果要切换多个元素,此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。也就是用一个<template>把那些元素包含起来。'
<div id = "app">
<template v-if="ok">
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
</div> //html
var vm = new Vue({
el:"#app",
data:{
ok:true
}
})
1.3 if else的用法
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
1.4 另外一个根据条件来渲染元素的选项的v-show指令
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
v-for
2.1 对一个列表进行渲染
v-for 指令需要使用 "item in items "形式的特殊语法,
items 是源数据数组并且 item 是数组元素迭代的别名。
也就是说我们原来实例化的对象的data对象中的数组的名字是复数的(例如是products)然后在html中用<li v-for = "product in products"></li>来实现我们的遍历。
当然你用item in products也是没问题的,用取到的item.text...进行操作,数组有index代表索引,对象有key代表索引(key,value)
<div id="app_3">
<ol>
<li v-for = "(todo,index) in todos">
{{ todo.text }} {{index}}
</li>
</ol>
</div> //有索引值的情况
<div id="app_3">
<ol>
<li v-for = "todo in todos">
{{ todo.text }}
</li>
</ol>
</div>//html
var app3 = new Vue({
el: '#app_3',
data: {
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
得到的结果是:
index从0开始
v-on
3.1 基本用法
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
看代码:
<div id="app_4">
<button v-on:click = "count = count+1">add</button>
<p>您已经点击{{count}}次</p>
</div> //html
var app4 = new Vue({
el:"#app_4",
data:{
count:0
}
})//script
3.2 v-on可以用来接收一个方法,我们应该在实例化对象的method中定义方法。
v-model
在表单 <input> 及 <textarea> 元素上创建双向数据绑定。可以监听用户的数据并且更新。
<div id="app_5">
<input v-model="message"placeholder="edit">
<p>您输入的信息是:{{message}}</p>
<!-- 文本框的双向数据绑定 -->
</div>//html
var app5 = new Vue({
el:"#app_5",
data:{
message:''
}
})//script
<textarea>也一样不过就是多行文本输入
v-bind
绑定class,href...
同一个标签不管用v-bind还是直接<a class="xxx">所绑定的类是不会冲突的。
<div id="app_7">
<a v-bind:href="link" v-bind:class="classA">to success</a>
</div>
var app7 = new Vue({
el:"#app_7",
data:{
link:"https://www.baidu.com",
classA:'red-fonts'
}
})
classA也是我们在data中的参数,可以在classA中指定。
还可以给classA加个属性,为true时就在这个类中,为false时就不在这个类中。
<a v-on:click.stop="aaa"></a>
.stop表示冒泡。.XXX是事件修改器。
小小总结
- v-on,@,methods事件绑定
- v-on修饰符可以指定键盘事件
- v-model表单数据模型双向绑定
子组件向父组件通信
- 新建的组件最好就放在我们的components目录下面
- 组件名字如果是驼峰形式,例如helloWorld,作为标签直接导入时就应该是hello-world(这是vue的特点,vue自动为你转化了)
-
要在父组件里面的components里面进行注册才能用
这个就是因为没有注册我们的组件的提醒。请注意:我们的组件都是在components下面注册的,components是和data,methods同一级的。
在html套用的时候要将驼峰形式改成用连接符连接的形式,例如helloWorld,作为标签直接导入时就应该是hello-world(这是vue的特点,vue自动为你转化了)
子组件通过$emit向父组件发送信息,触发事件,具体步骤如下:
在子组件中进行< content @click=”close”>事件的绑定
在子组件中的methods对这个close进行定义
methods:{ close(){ this.emit(‘on-close’) } }
在父组件中的子组件标签中绑定on-close这个事件
<dialog :on-close=”beClose”>
-
在父组件的methods定义这个事件
methods{ beClose(){ console.log(10) } }
父组件向子组件通信
父组件向子组件传递信息时,要在子组件的props里面进行定义
父组件中向子组件传number=5;
需要在子组件里面的props进行声明,
props中多个单词之间要用连接符不要用驼峰方式
如果要实现动态绑定也十分简单,注意要绑定,用v-bind或者:
用于一些幻灯片组件的使用啊,具体的数据在父组件那里的时候
父组件向子组件传递信息的另外一个方式是插槽<slot></slot>
结果是:并没有显示出来
我们应该在子组件中相对应的要显示的地方加上<slot>标签。
小小的总结:
父组件向内传递属性-动态属性
子组件向外发布事件
slot插槽传递模板-具名slot
关于vue-router
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
在html中,我们就可以用<router-link>来进行导航
<router-view/>是渲染出口
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
在实例化的时候, mode: 'history',可以后退。
前端路由参数
在映射表中进行设置
color会被做为参数注入到$route.params里面
Vuex
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
注意事项
- 记得注册store!!!!!不然commit,mutation这些它是识别不出来的啊!state也是重点来着
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):
同步逻辑放在mutation中
异步逻辑放在action中
然后就可以去调用store里面的属性。
https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart
优雅的使用v-for 、computed
注意绑定啊啊啊啊
没有:就直接将isShowDialog作为值传递进去,这是不行的
双向数据绑定
实例中演示了 input 和 textarea 元素中使用 v-model 实现双向数据绑定
另一种方式实现 Vue 的响应式原理
众所周知,vue是通过Object.defineProperty 追踪依赖,从而实现响应式的。
有两个不足之处:
- 不能检测到增加或删除的属性。
- 数组方面的变动,如根据索引改变元素,以及直接改变数组长度时的变化,不能被检测到。
原因差不多,无非就是没有被 getter/setter 。
第一个比较容易理解,为什么数组长度不能被 getter/setter ?
在知乎上找了一个答案:如果你知道数组的长度,理论上是可以预先给所有的索引设置 getter/setter 的。但是一来很多场景下你不知道数组的长度,二来,如果是很大的数组,预先加 getter/setter 性能负担较大。
现在有一个替代的方案 Proxy,但这东西兼容性不好,迟早要上的。
Proxy,在目标对象之前架设一层拦截。具体,可以参考 http://es6.ruanyifeng.com/#docs/reference
Vue3.0就用的proxy
vue打包有时候会找不到图片,主要是打包路径的问题
https://www.cnblogs.com/diantao/p/7776523.html
更新一下对于computed的认识
其实主要还是为了简便计算
例如我们有一个这样的需求: 将输入两个文本框的姓氏和名字一起显示出来。
<input type="text"
v-model="firstName" />
<input type="text"
v-model="lastName" />
<p>{{name}}</p>
data () {
return {
msg: 'Welcome to Your Vue.js App',
firstName: '',
lastName: ''
}
},
computed: {
name () {
return this.firstName + this.lastName
}
}
只要我们在computed里面定义的这个name函数已经将firstName和lastName一起拼合起来就好了,然后就可以在标签里面直接用name了。
Vue生命周期
我们先来说说几个最经常用到的。
- created
实例(APP)已经创建,变量还未渲染到模板
此时修改变量还是能起作用的 - mounted
变量已经挂载到模板上面了,此时修改变量也还是能起作用的 - updated
实例更新 - destoryed
实例销毁
面试题:为什么要使用VUEJS
实现项目的模块化,组件化。
实现数据渲染。
提供路由,ajax,数据流等其他功能。