Node.js:
基于事件循环的异步IO
单线程运行,避免多线程的变量同步问题
JS可以编写后台代码,前后台统一编程语言
node.js的伟大之处不在于让JS迈向了后端开发,而是构建了一个庞大的生态系统。
NPM是node.js的包管理系统。
MVVM模式
M:即Model,模型,包括数据和一些基本操作
V:即View,视图,页面渲染结果
VM:即View-Model,模型与视图间的双向绑定(无需dom操作)
vm:
当用户修改了View,Model中的数据也会跟着改变。
把关注点放在如何操作Model上。
(node基础上开发的前端框架)
官网:https://cn.vuejs.org/v2/guide/index.html
Node和NPM
NPM是Node提供的模块管理工具,可以非常方便的下载安装很多前端框架,包括Jquery、AngularJS、VueJs都有。
推荐下载LTS版本:node -v查看版本
Node自带了NPM了,在控制台输入npm -v
查看:
-g
代表全局安装)npm install nrm -g
然后通过nrm ls
命令查看npm的仓库列表;
通过nrm use taobao
来指定要使用的镜像源
注意:
不建议使用cnpm命令,可能存在bug。
安装完成请一定要重启下电脑!!!
快速搭建vue项目的脚手架:vue-cli,使用它能快速的构建一个web工程模板。
安装命令:
npm install -g vue-cli
创建一个新的空工程empty Project, 然后新建一个module, 选中static web,静态web项目
用vue-cli命令在项目的目录下,快速搭建一个webpack的项目:
vue init webpack
最后三项不选:eslint、unit tests 、e2e tests with Nightwatch,,都为no
入口文件:main.js
单文件组件:
每一个.vue文件,就是一个独立的vue组件。都有自己独立的html、JS、CSS
package.json:
- scripts:运行脚本 ;
- dependencies:运行时依赖(打包到项目中);
- devDependencies:开发时依赖(仅开发需要);
常用:
npm run dev :
启动项目;- webpack-dev-server:开发时热更新服务使用,可以帮我们运行一个web服务,加载页面内容,并且修改js后不需要重新加载就能看到最新结果。
- build:等同于webpack的打包功能,会打包到dist目录下
- npm install :安装依赖
{{name}},{{num}}
new Vue()来创建Vue实例
然后构造函数接收一个对象,并且在对象中声明各种Vue需要的数据和方法:el、data、methods等
有一个
input
元素,通过v-model
与num
进行绑定。这里用
v-on
指令绑定点击事件,可以直接操作num写方法一定要简写方式add(){}或者箭头函数,这样里面的this代表的才是Vue实例。
add:function(){} js中的this代表的是调用方,window
定义的数据、方法只在这个vue的实例中有效。
每个Vue实例都需要关联一段Html模板并进行视图渲染。通过el属性来指定。
这样,Vue就可以基于id为app
的div元素作为模板进行渲染了。在这个div范围以外的部分是无法使用vue特性的。
data里面的属性和视图上是双向绑定的。哪一方的值变化,另一方跟着变。
Vue为生命周期中的每个状态都设置了钩子函数(监听函数)。每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
5.1.钩子函数
beforeCreated:vue实例化之前调用。
created:创建实例之后进行调用。常用(页面还没有渲染,可以这个时候去数据库获取数据)
beforeMount:页面加载完成,没有渲染。如:此时页面还是{{name}}
mounted:页面已经渲染, 此时页面中的{{name}}已被渲染成实例中的数据。
beforeDestroy:该函数将在销毁实例前进行调用 。
destroyed:改函数将在销毁实例时进行调用。
beforeUpdate:组件中的数据更新之前调用。
updated:组件中的数据更新之后调用。
...if 、for、v-bind、v-on等等。
{{表达式}}
说明:
该表达式支持JS语法,可以调用js内置函数(必须有返回值)
表达式必须有返回结果;
可以直接获取Vue实例中定义的数据或函数
7.1插值闪烁
使用{{}}方式在网速较慢时, 数据未加载完成时,页面会显示出原始的{{}}
,加载完毕后才显示正确数据,我们称为插值闪烁。
v-text和v-html(解决插值闪烁的问题) 单向绑定 数据影响---->视图
使用v-text和v-html指令来替代{{}},
当没有数据时,会显示空白。
v-text:将数据输出到元素内部,如果输出的数据有HTML代码,会作为普通文本输出
v-html:将数据输出到元素内部,如果输出的数据有HTML代码,会被渲染
v-model是双向绑定,视图(View)和模型(Model)之间会互相影响。
既然是双向绑定,一定是在视图中可以修改数据,这样就限定了视图的元素类型。目前v-model的可使用元素有:
input
select
textarea
checkbox
radio
components(Vue中的自定义组件)
基本上除了最后一项,其它都是表单的输入项。
举例:
html:
Java
PHP
Swift
你选择了:{{language.join(',')}}
多个CheckBox
对应一个model时,model的类型是一个数组,单个checkbox值默认是boolean类型
radio对应的值是input的value值
input
和textarea
默认对应的model是字符串
select
单选对应字符串,多选对应也是数组
给页面元素绑定事件。
语法:
v-on:事件名="js片段或函数名"
@事件名="js片段或函数名"
v-on:click='add'
可以简写为@click='add'
9.1.事件修饰符
.stop
:阻止事件冒泡到父元素
.prevent
:阻止默认事件发生
.capture
:使用事件捕获模式
.self
:只有元素自身触发事件才执行。(冒泡或捕获的都不执行)
.once
:只执行一次
阻止默认事件
有{{num}}
效果:(右键“增加一个”,不会触发默认的浏览器右击事件;右键“减少一个”,会触发默认的浏览器右击事件)
事件冒泡:如果子元素和父元素都绑定了一个点击事件,当你使用事件冒泡时,子级元素先触发,父级元素后触发。如果触发子元素事件不想父元素事件触发需要阻止事件冒泡。
事件捕获: 如果子元素和父元素都绑定了一个点击事件,当你使用事件捕获时,父级元素先触发,子级元素后触发.
默认事件:很多的网页元素都会有默认的行为,比如说当你点击一下超链接a标签的时候,它会有一个跳转的行为;当你在网页上点鼠标右键时会出现一个右键菜;当你在一个form表单里点击提交按钮时网页会产生提交行为并刷新网页,当你网页上滚动鼠标滚轮时,网页的滚动条会动等等。这些都叫事件的默认行为。
10.1.遍历数组
v-for="item in items"
items:要遍历的数组,需要在vue的data中定义好。
item:迭代得到的数组元素的别名
v-for="(item,index) in items"
items:要迭代的数组
item:迭代得到的数组元素别名
index:迭代到的当前元素索引,从0开始。
10.2.遍历对象
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
1个参数时,得到的是对象的属性
2个参数时,第一个是属性,第二个是键
3个参数时,第三个是索引,从0开始
-
{{index + 1}}. {{key}} - {{value}}
10.3.key
当 Vue.js 用 v-for
正在更新已渲染过的元素列表时,它默认用“就地复用”策略。但是要实现这个功能,你需要给Vue一些提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
属性。理想的 key
值是每项都有的且唯一的 id。
:key=""
:读取vue中的属性,并赋值给key属性
为true时,所在的元素才会被渲染。
v-if="布尔表达式"
11.1.与v-for结合
当v-if和v-for出现在一起时,v-for优先级更高。也就是说,会先遍历,再判断条件。
修改v-for中的案例,添加v-if:
-
{{index + 1}}. {{user.name}} - {{user.gender}} - {{user.age}}
效果:只显示女性用户信息。
11.2.v-else
v-else
元素必须紧跟在带v-if
或者v-else-if
的元素的后面,否则它将不会被识别。
v-else-if
,顾名思义,充当v-if
的“else-if 块”,可以连续使用;
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
Hello!
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。
html属性不能使用双大括号形式绑定,只能使用v-bind指令。
在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
13.1.绑定class样式
通常情况下,绑定的数据对象不必内联定义在模板里:
数据:
data: {
classObject: {
active: true,
'text-danger': false
}
}
效果和之前一样:
13.2.绑定style样式
直接绑定到一个样式对象通常更好,这会让模板更清晰:
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
13.3.简写
v-bind:class
可以简写为:class
Vue中提供了计算属性,来替代复杂的表达式:
var vm = new Vue({
el:"#app",
data:{
birthday:1429032123201 // 毫秒值
},
computed:{
birth(){// 计算属性本质是一个方法,但是必须返回结果
const d = new Date(this.birthday);
return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDay();
}
}
})
计算属性本质就是方法,但是一定要返回数据。然后页面渲染时,可以把这个方法当成一个变量来使用。
页面使用:
您的生日是:{{birth}}
我们可以将同一函数定义为一个方法而不是一个计算属性。
计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要birthday
还没有发生改变,多次访问 birthday
计算属性会立即返回之前的计算结果,而不必再次执行函数。
watch可以让我们监控一个值的变化。从而做出相应的反应。
示例:
我们通过Vue的component方法来定义一个全局组件。
组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
组件渲染需要html模板,所以增加了template属性,值就是HTML模板,没有el属性。
全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
data必须是一个函数,不再是一个对象。
定义好的组件,可以任意复用多次:
一个组件的 data 选项必须是一个函数,每个实例维护一份被返回对象的独立的拷贝:
data: function () {
return {
count: 0
}
}
相当于全局属性变成局部属性, data的属性只在组件中有效。
一旦全局注册随着Vue的加载而加载。不频繁使用的组件,我们会采用局部注册。
const counter = {
template:'',
data(){
return {
count:0
}
}
};
var app = new Vue({
el:"#app",
components:{
counter:counter // 将定义的对象注册为组件
}
})
components就是当前vue对象子组件集合。
其key就是子组件名称
其值就是组件对象的属性
这个counter组件只能在当前的Vue实例中使用
通常一个单页应用会以一棵嵌套的组件树的形式来组织:
页面首先分成了顶部导航、左侧内容区、右侧边栏三部分
左侧内容区又分为上下两个组件
右侧边栏中又包含了3个子组件
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
父组件使用子组件时,自定义属性(属性名任意,属性值为要传递的数据)
子组件通过props接收父组件属性。
父组件使用子组件,并自定义了title属性:
打个招呼:
我们定义一个子组件,并接受复杂数据:
const myList = {
template: '\
\
- {{item.id}} : {{item.name}}
\
\
',
props: {
items: {
type: Array,
default: [],
required: true
}
}
};
这个子组件可以对 items 进行迭代,并输出到页面。
props:定义需要从父组件中接收的属性
items:是要接收的属性名称
type:限定父组件传递来的必须是数组
default:默认值
required:是否必须
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
我们在父组件中使用它:
已开设如下课程:
var app = new Vue({
el:"#app",
components:{
myList // 当key和value一样时,可以只写一个
},
data:{
lessons:[
{id:1, name: 'java'},
{id:2, name: 'php'},
{id:3, name: 'ios'},
]
}
})
type类型,可以有:
给 prop 传入一个动态的值: (通过v-bind从数据模型中,获取title的值)
num: {{num}}
子组件接收父组件的num属性
子组件定义点击按钮,点击后对num进行加或减操作
我们尝试运行,好像没问题,点击按钮试试:
子组件接收到父组件属性后,默认是不允许修改的。
只有父组件能修改,那么加和减的操作一定是放在父组件:
var app = new Vue({
el:"#app",
data:{
num:0
},
methods:{ // 父组件中定义操作num的方法
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数:
我们可以通过v-on指令将父组件的函数绑定到子组件上:
num: {{num}}
在子组件中定义函数,函数的具体实现调用父组件的实现,并在子组件中调用这些函数。当子组件中按钮被点击时,调用绑定的函数:
Vue.component("counter", {
template:'\
\
\
\
',
props:['count'],
methods:{
plus(){
this.$emit("inc");
},
reduce(){
this.$emit("dec");
}
}
})
vue提供了一个内置的this.$emit()函数,用来调用父组件绑定的函数。
但是为了复用性,开发中都会把组件放入独立的JS文件中,我们新建一个user目录以及login.js及register.js:
编写组件,这里我们只写模板,不写功能。
login.js内容如下:
const loginForm = {
template:'\
\
登录页
\
用户名:
\
密码:
\
\
'
}
register.js内容:
const registerForm = {
template:'\
\
注册页
\
用 户 名:
\
密 码:
\
确认密码:
\
\
'
}
登录
注册
实现 复杂单页应用的动态路由功能。
新建vue-router对象,并且指定路由规则:
// 创建VueRouter对象
const router = new VueRouter({
routes:[ // 编写路由规则
{
path:"/login", // 请求路径
component:loginForm // 组件名称
},
{
path:"/register",
component:registerForm
},
]
})
routes:路由规则的数组,可以指定多个对象,每个对象是一条路由规则。
在父组件中引入router对象:
var vm = new Vue({
el:"#app",
components:{// 引用登录和注册组件
loginForm,
registerForm
},
router // 引用上面定义的router对象
})
页面跳转控制:
登录
注册
通过
来指定一个锚点,当路由的路径匹配时,vue-router会自动把对应组件放到锚点位置进行渲染
通过
指定一个跳转链接,当点击时,会触发vue-router的路由功能,路径中的hash值会随之改变
注意:单页应用中,页面的切换并不是页面的跳转。仅仅是地址最后的hash值变化。
事实上,我们总共就一个HTML:index.html
https://webpack.docschina.org/configuration/
四个核心概念:
入口(entry)
webpack打包的起点,可以有一个或多个,一般是js文件。webpack会从启点文件开始,寻找启点直接或间接依赖的其它所有的依赖,包括JS、CSS、图片资源等,作为将来打包的原始数据
输出(output) 打包成build.js
出口一般包含两个属性:path和filename。用来告诉webpack打包的目标文件夹,以及文件的名称。目的地也可以有多个。
加载器(loader) 可选
webpack本身只识别Js文件,如果要加载非JS文件,必须指定一些额外的加载器(loader),例如css-loader。然后将这些文件转为webpack能处理的有效模块,最后利用webpack的打包能力去处理。
插件(plugins) 可选
插件可以扩展webpack的功能,让webpack不仅仅是完成打包,甚至各种更复杂的功能,或者是对打包功能进行优化、压缩,提高效率。
vue-router使用模块化加载后,必须增加一句:Vue.use(VueRouter)
入口一般是在根目录src下的main.js
打包:npm run build
热更新的web服务(webpack-dev-server
)
"scripts": {
"dev": "webpack-dev-server --inline --hot --open --port 8080 --host 127.0.0.1"
},
- --inline:自动刷新
- --hot:热加载
- --port:指定端口
- --open:自动在默认浏览器打开
- --host:可以指定服务器的 ip,不指定则为127.0.0.1