Node.js:
基于事件循环的异步IO
单线程运行,避免多线程的变量同步问题
JS可以编写后台代码,前后台统一编程语言
node.js的伟大之处不在于让JS迈向了后端开发,而是构建了一个庞大的生态系统。
NPM是node.js的包管理系统。
MVVM模式
M:即Model,模型,包括数据和一些基本操作
V:即View,视图,页面渲染结果
VM:即View-Model,模型与视图间的双向绑定(无需dom操作)
vm:
当用户修改了View,Model中的数据也会跟着改变。
把关注点放在如何操作Model上。
vue-cli
快速搭建vue项目的脚手架:vue-cli,使用它能快速的构建一个web工程模板。
安装命令:
npm install -g vue-cli
常用命令:
npm run dev :
启动项目;- webpack-dev-server:开发时热更新服务使用,可以帮我们运行一个web服务,加载页面内容,并且修改js后不需要重新加载就能看到最新结果。
- build:等同于webpack的打包功能,会打包到dist目录下
- npm install :安装依赖
Vue语法
1.vue声明式渲染和双向数据绑定
new Vue()来创建Vue实例
然后构造函数接收一个对象,并且在对象中声明各种Vue需要的数据和方法:el、data、methods等
有一个
input
元素,通过v-model
与num
进行绑定。这里用
v-on
指令绑定点击事件,可以直接操作num写方法一定要简写方式add(){}或者箭头函数,这样里面的this代表的才是Vue实例。
add:function(){} js中的this代表的是调用方,window
定义的数据、方法只在这个vue的实例中有效。
2.模板或元素
每个Vue实例都需要关联一段Html模板并进行视图渲染。通过el属性来指定。
这样,Vue就可以基于id为app
的div元素作为模板进行渲染了。在这个div范围以外的部分是无法使用vue特性的。
3.数据
data里面的属性和视图上是双向绑定的。哪一方的值变化,另一方跟着变。
4.方法
5.生命周期钩子
Vue为生命周期中的每个状态都设置了钩子函数(监听函数)。每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
5.1.钩子函数
beforeCreated:vue实例化之前调用。
created:创建实例之后进行调用。常用(页面还没有渲染,可以这个时候去数据库获取数据)
beforeMount:页面加载完成,没有渲染。如:此时页面还是{{name}}
mounted:页面已经渲染, 此时页面中的{{name}}已被渲染成实例中的数据。
beforeDestroy:该函数将在销毁实例前进行调用 。
destroyed:改函数将在销毁实例时进行调用。
beforeUpdate:组件中的数据更新之前调用。
updated:组件中的数据更新之后调用。
6.指令(v-)常用
...if 、for、v-bind、v-on等等。
7.插值表达式
{{表达式}}
说明:
该表达式支持JS语法,可以调用js内置函数(必须有返回值)
表达式必须有返回结果;
可以直接获取Vue实例中定义的数据或函数
7.1插值闪烁
使用{{}}方式在网速较慢时, 数据未加载完成时,页面会显示出原始的{{}}
,加载完毕后才显示正确数据,我们称为插值闪烁。
v-text和v-html(解决插值闪烁的问题) 单向绑定 数据影响---->视图
使用v-text和v-html指令来替代{{}},
当没有数据时,会显示空白。
v-text:将数据输出到元素内部,如果输出的数据有HTML代码,会作为普通文本输出
v-html:将数据输出到元素内部,如果输出的数据有HTML代码,会被渲染
8.v-model
v-model是双向绑定,视图(View)和模型(Model)之间会互相影响。
既然是双向绑定,一定是在视图中可以修改数据,这样就限定了视图的元素类型。目前v-model的可使用元素有:
input
select
textarea
checkbox
radio
components(Vue中的自定义组件)
基本上除了最后一项,其它都是表单的输入项。
举例:
html:
<div id="app">
<input type="checkbox" v-model="language" value="Java" />Java<br/>
<input type="checkbox" v-model="language" value="PHP" />PHP<br/>
<input type="checkbox" v-model="language" value="Swift" />Swift<br/>
<h1>
你选择了:{{language.join(',')}}
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
language: []
}
})
</script>
-
多个
CheckBox
对应一个model时,model的类型是一个数组,单个checkbox值默认是boolean类型 -
radio对应的值是input的value值
-
input
和textarea
默认对应的model是字符串 -
select
单选对应字符串,多选对应也是数组
9.v-on
给页面元素绑定事件。
语法:
v-on:事件名="js片段或函数名"
@事件名="js片段或函数名"
v-on:click='add'
可以简写为@click='add'
9.1.事件修饰符
.stop
:阻止事件冒泡到父元素
.prevent
:阻止默认事件发生
.capture
:使用事件捕获模式
.self
:只有元素自身触发事件才执行。(冒泡或捕获的都不执行)
.once
:只执行一次
阻止默认事件
<div id="app">
<!--右击事件,并阻止默认事件发生-->
<button @contextmenu.prevent="num++">增加一个</button>
<br/>
<!--右击事件,不阻止默认事件发生-->
<button @contextmenu="decrement($event)">减少一个</button>
<br/>
<h1>有{{num}}</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
num: 100
},
methods: {
decrement(ev) {
// ev.preventDefault();
this.num--;
}
}
})
</script>
效果:(右键“增加一个”,不会触发默认的浏览器右击事件;右键“减少一个”,会触发默认的浏览器右击事件)
事件冒泡:如果子元素和父元素都绑定了一个点击事件,当你使用事件冒泡时,子级元素先触发,父级元素后触发。如果触发子元素事件不想父元素事件触发需要阻止事件冒泡。
事件捕获: 如果子元素和父元素都绑定了一个点击事件,当你使用事件捕获时,父级元素先触发,子级元素后触发.
默认事件:很多的网页元素都会有默认的行为,比如说当你点击一下超链接a标签的时候,它会有一个跳转的行为;当你在网页上点鼠标右键时会出现一个右键菜;当你在一个form表单里点击提交按钮时网页会产生提交行为并刷新网页,当你网页上滚动鼠标滚轮时,网页的滚动条会动等等。这些都叫事件的默认行为。
10.v-for
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开始
<div id="app">
<ul>
<li v-for="(value, key, index) in user">
{{index + 1}}. {{key}} - {{value}}
</li>
</ul>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
user:{name:'松哥', gender:'男', age: 18}
}
})
</script>
10.3.key
当 Vue.js 用 v-for
正在更新已渲染过的元素列表时,它默认用“就地复用”策略。但是要实现这个功能,你需要给Vue一些提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
属性。理想的 key
值是每项都有的且唯一的 id。
<ul>
<li v-for="(item,index) in items" :key=index></li>
</ul>
-
:key=""
:读取vue中的属性,并赋值给key属性
11.v-if和v-show
为true时,所在的元素才会被渲染。
v-if="布尔表达式"
11.1.与v-for结合
当v-if和v-for出现在一起时,v-for优先级更高。也就是说,会先遍历,再判断条件。
修改v-for中的案例,添加v-if:
<ul>
<li v-for="(user, index) in users" v-if="user.gender == '女'">
{{index + 1}}. {{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
效果:只显示女性用户信息。
11.2.v-else
v-else
元素必须紧跟在带v-if
或者v-else-if
的元素的后面,否则它将不会被识别。
v-else-if
,顾名思义,充当v-if
的“else-if 块”,可以连续使用;
12.v-show
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。
13.v-bind(html属性绑定)
html属性不能使用双大括号形式绑定,只能使用v-bind指令。
在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
<div id="app">
<!--可以是数据模型,可以是具有返回值的js代码块或者函数-->
<div v-bind:title="title" style="border: 1px solid red; width: 50px; height: 50px;"></div>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
title: "title",
}
})
</script>
13.1.绑定class样式
通常情况下,绑定的数据对象不必内联定义在模板里:
<div class="static" v-bind:class="classObject"></div>
数据:
data: {
classObject: {
active: true,
'text-danger': false
}
}
效果和之前一样:
<div class="static active"></div>
13.2.绑定style样式
直接绑定到一个样式对象通常更好,这会让模板更清晰:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
13.3.简写
v-bind:class
可以简写为:class
14.计算属性
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();
}
}
})
-
计算属性本质就是方法,但是一定要返回数据。然后页面渲染时,可以把这个方法当成一个变量来使用。
页面使用:
<div id="app">
<h1>您的生日是:{{birth}} </h1>
</div>
我们可以将同一函数定义为一个方法而不是一个计算属性。
计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要birthday
还没有发生改变,多次访问 birthday
计算属性会立即返回之前的计算结果,而不必再次执行函数。
14.watch
watch可以让我们监控一个值的变化。从而做出相应的反应。
示例:
<div id="app">
<input type="text" v-model="message">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:""
},
watch:{
message(newVal, oldVal){
console.log(newVal, oldVal);
}
}
})
</script>
6.组件化
6.1.全局组件
我们通过Vue的component方法来定义一个全局组件。
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
// 定义全局组件,两个参数:1,组件名称。2,组件参数
Vue.component("counter",{
template:'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>',
data(){
return {
count:0
}
}
})
var app = new Vue({
el:"#app"
})
</script>
组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
组件渲染需要html模板,所以增加了template属性,值就是HTML模板,没有el属性。
全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
data必须是一个函数,不再是一个对象。
6.2.组件的复用
定义好的组件,可以任意复用多次:
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
一个组件的 data 选项必须是一个函数,每个实例维护一份被返回对象的独立的拷贝:
data: function () {
return {
count: 0
}
}
相当于全局属性变成局部属性, data的属性只在组件中有效。
6.3.局部注册
一旦全局注册随着Vue的加载而加载。不频繁使用的组件,我们会采用局部注册。
const counter = {
template:'<button v-on:click="count++">你点了我 {{ count }} 次,我记住了.</button>',
data(){
return {
count:0
}
}
};
var app = new Vue({
el:"#app",
components:{
counter:counter // 将定义的对象注册为组件
}
})
-
components就是当前vue对象子组件集合。
-
其key就是子组件名称
-
其值就是组件对象的属性
-
这个counter组件只能在当前的Vue实例中使用
-
6.4.组件通信
通常一个单页应用会以一棵嵌套的组件树的形式来组织:
-
页面首先分成了顶部导航、左侧内容区、右侧边栏三部分
-
左侧内容区又分为上下两个组件
-
右侧边栏中又包含了3个子组件
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
6.4.1.props(父向子传递)
-
父组件使用子组件时,自定义属性(属性名任意,属性值为要传递的数据)
-
子组件通过props接收父组件属性。
父组件使用子组件,并自定义了title属性:
<div id="app">
<h1>打个招呼:</h1>
<!--使用子组件,同时传递title属性-->
<introduce title="大家好,我是锋哥"/>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("introduce",{
// 直接使用props接收到的属性来渲染页面
template:'<h1>{{title}}</h1>',
props:['title'] // 通过props来接收一个父组件传递的属性
})
var app = new Vue({
el:"#app"
})
</script>
6.4.2.props验证
我们定义一个子组件,并接受复杂数据:
const myList = {
template: '\
<ul>\
<li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
</ul>\
',
props: {
items: {
type: Array,
default: [],
required: true
}
}
};
-
这个子组件可以对 items 进行迭代,并输出到页面。
-
props:定义需要从父组件中接收的属性
-
items:是要接收的属性名称
-
type:限定父组件传递来的必须是数组
-
default:默认值
-
required:是否必须
-
-
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
我们在父组件中使用它:
<div id="app">
<h2>已开设如下课程:</h2>
<!-- 使用子组件的同时,传递属性,这里使用了v-bind,指向了父组件自己的属性lessons -->
<my-list :items="lessons"/>
</div>
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类型,可以有:
6.4.3.动态传递
给 prop 传入一个动态的值: (通过v-bind从数据模型中,获取title的值)
<introduce :title="title"/>
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个JavaScript表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post>
6.4.4.子向父的通信
<div id="app">
<h2>num: {{num}}</h2>
<!--使用子组件的时候,传递num到子组件中-->
<counter :num="num"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("counter", {// 子组件,定义了两个按钮,点击数字num会加或减
template:'\
<div>\
<button @click="num++">加</button> \
<button @click="num--">减</button> \
</div>',
props:['num']// count是从父组件获取的。
})
var app = new Vue({
el:"#app",
data:{
num:0
}
})
</script>
-
子组件接收父组件的num属性
-
子组件定义点击按钮,点击后对num进行加或减操作
我们尝试运行,好像没问题,点击按钮试试:
子组件接收到父组件属性后,默认是不允许修改的。
只有父组件能修改,那么加和减的操作一定是放在父组件:
var app = new Vue({
el:"#app",
data:{
num:0
},
methods:{ // 父组件中定义操作num的方法
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数:
我们可以通过v-on指令将父组件的函数绑定到子组件上:
<div id="app">
<h2>num: {{num}}</h2>
<counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>
在子组件中定义函数,函数的具体实现调用父组件的实现,并在子组件中调用这些函数。当子组件中按钮被点击时,调用绑定的函数:
Vue.component("counter", {
template:'\
<div>\
<button @click="plus">加</button> \
<button @click="reduce">减</button> \
</div>',
props:['count'],
methods:{
plus(){
this.$emit("inc");
},
reduce(){
this.$emit("dec");
}
}
})
-
vue提供了一个内置的this.$emit()函数,用来调用父组件绑定的函数。
7.路由vue-router
7.1.2.编写登录及注册组件
但是为了复用性,开发中都会把组件放入独立的JS文件中,我们新建一个user目录以及login.js及register.js:
编写组件,这里我们只写模板,不写功能。
login.js内容如下:
const loginForm = {
template:'\
<div>\
<h2>登录页</h2> \
用户名:<input type="text"><br/>\
密码:<input type="password"><br/>\
</div>\
'
}
register.js内容:
const registerForm = {
template:'\
<div>\
<h2>注册页</h2> \
用 户 名:<input type="text"><br/>\
密  码:<input type="password"><br/>\
确认密码:<input type="password"><br/>\
</div>\
'
}
7.1.3.在父组件中引用
<div id="app">
<span>登录</span>
<span>注册</span>
<hr/>
<div>
<!--<loginForm></loginForm>-->
<!--
疑问:为什么不采用上面的写法?
由于html是大小写不敏感的,如果采用上面的写法,则被认为是<loginform></loginform>
所以,如果是驼峰形式的组件,需要把驼峰转化为“-”的形式
-->
<login-form></login-form>
<register-form></register-form>
</div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="user/login.js"></script>
<script src="user/register.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
components: {
loginForm,
registerForm
}
})
</script>
vue-router
实现 复杂单页应用的动态路由功能。
新建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对象
})
页面跳转控制:
<div id="app">
<!--router-link来指定跳转的路径-->
<span><router-link to="/login">登录</router-link></span>
<span><router-link to="/register">注册</router-link></span>
<hr/>
<div>
<!--vue-router的锚点-->
<router-view></router-view>
</div>
</div>
-
通过
<router-view>
来指定一个锚点,当路由的路径匹配时,vue-router会自动把对应组件放到锚点位置进行渲染 -
通过
<router-link>
指定一个跳转链接,当点击时,会触发vue-router的路由功能,路径中的hash值会随之改变
注意:单页应用中,页面的切换并不是页面的跳转。仅仅是地址最后的hash值变化。
事实上,我们总共就一个HTML:index.html
webpack
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