Vue.js
https://cn.vuejs.org/
Javascript开发常见需求
MVVM
组件
MVVM模式
Vue.js,Angular.js,React.js,Ember.js
数据驱动和组件式编程
数据驱动
组件式编程
这个理念不是来源于vue, 把web组件式开发发扬光大的应该是react了,组件开发是一种朴素的开发思想,分而治之,大型系统拆分成一个个的小模块小组件,分配给不同的人。额外的好处是顺便能复用这个组件。
组件理解
理解组件的思想可以类比函数。一个函数包含哪些东西呢?
形参
局部变量
函数名
返回值
那对应到vue中又是什么呢?
函数
Vue组件
形参
Slot,props
局部变量和局部函数
Data, methods
函数名
Name
Return
template
Hello Vue
示例 程序结构
< script src = "vue.global.js" ></ script >
<!-- 定义View -->
< div id = "app" > {{ message }}</ div >
< script >
const { createApp } = Vue
//创建ViewModel
createApp ({
data () { //定义Model
return {
message : 'Hello Vue!'
}
}
}). mount ( '#app' ) //绑定
</ script >
使用Vue的过程就是定义MVVM各个组成部分的过程的过程。
定义View
定义Model
创建一个Vue实例或"ViewModel",它用于连接View和Model
在创建Vue实例时,需要传入一个选项对象,选项对象可以包含数据、挂载元素、方法、模生命周期钩子等等。
在这个示例中,选项对象的el属性指向View,el: '#app'表示该Vue实例将挂载到<div id="app">...</div>
这个元素;data属性指向Model,data: exampleData表示我们的Model是exampleData对象。
Vue.js有多种数据绑定的语法,最基础的形式是文本插值,使用一对大括号语法,在运行时{{ message }}会被数据对象的message属性替换,所以页面上会输出"Hello World!"。
生命周期
生命周期 主要阶段
每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如,实例需要配置数据观测(data observer)、编译模版、挂载实例到 DOM ,然后在数据变化时更新 DOM 。在这个过程中,实例也会调用一些 生命周期钩子 ,这就给我们提供了执行自定义逻辑的机会。
它可以总共分为8个阶段:
beforeCreate:在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created:实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
mounted: el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。该钩子在服务器端渲染期间不被调用。
beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。该钩子在服务器端渲染期间不被调用。
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。该钩子在服务器端渲染期间不被调用。
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
API风格
模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析
Vue.js常用指令
组件化应用构建
全局注册
import { createApp } from 'vue'
const app = createApp ({})
app . component (
// 注册的名字
'MyComponent' ,
// 组件的实现
{
/* ... */
}
)
局部注册
< script setup >
import ComponentA from './ComponentA.vue'
< /script>
< template >
< ComponentA />
< /template>
风格指南
https://cn.vuejs.org/v2/style-guide/
组件名为多个单词
Vue . component ( 'todo-item' , {
props : [ 'todo' ],
template : '<li>{{ todo.text }}</li>'
})
单文件组件文件的大小写
Vue.js快速起步
https://cn.vuejs.org/guide/quick-start.html
安装
npm -v //查看版本
npm install -g cnpm --registry= https://registry.npm.taobao.org //淘宝定制版
cnpm install vue //安装Vue
cnpm install --global vue-cli //全局安装 vue-cli
vue init webpack my-project //初始化项目
cd my-project //进入项目目录
cnpm install //安装依赖
cnpm run dev //运行
Vue.js目录结构
目录
说明
build
项目构建(webpack)相关代码
config
配置目录,包括端口号等。我们初学可以使用默认的。
node_modules
npm 加载的项目依赖模块
src
这里是我们要开发的目录,基本上要做的事情都在这个目录里。
src/assets
放置一些图片,如logo等。
src/components
目录里面放了一个组件文件,可以不用。
src/App.vue
项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。
src/main.js
项目的核心文件。
static
静态资源目录,如图片、字体等。
test
初始测试目录,这些是一些配置文件,包括语法配置,git配置等。
index.html
首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
package.json
项目配置文件。
README.md
项目的说明文档,markdown 格式
不同技术选型对比
纯Javascript Vue.js Vuetify.js
js-version.html <!DOCTYPE html>
< html >
< body onload = "onInput()" >
< div id = "app" >
< h4 > 数据的双向绑定示例:加法器----js的实现</ h4 >
< div class = "item" >
< span > 数1:</ span >
< input id = "d1" type = "number" onchange = "onInput()" />
</ div >
< div class = "item" >
< span > 数2:</ span >
< input id = "d2" type = "number" onchange = "onInput()" />
</ div >
< div class = "item" >
< span > 结果:</ span >
< span id = "result" ></ span >
</ div >
< div id = "observer1" >
< span > 用户操作:</ span > 数1:< span id = "observer1_d1" ></ span > ,数2:< span id = "observer1_d2" ></ span > ,结果是:< span
id = "observer1_result" ></ span >
</ div >
< div id = "observer2" >
< span > 也就是说:</ span >< span id = "observer2_d1" ></ span > +< span id = "observer2_d2" ></ span > =< span
id = "observer2_result" ></ span >
</ div >
</ div >
</ body >
</ html >
< script >
function onInput () {
//提取数据
var $d1 = document . getElementById ( "d1" );
var $d2 = document . getElementById ( "d2" );
//观察者
var $result = document . getElementById ( "result" );
var $observer1 = document . getElementById ( "observer1" );
var $observer1_d1 = document . getElementById ( "observer1_d1" );
var $observer1_d2 = document . getElementById ( "observer1_d2" );
var $observer1_result = document . getElementById ( "observer1_result" );
var $observer2 = document . getElementById ( "observer2" );
var $observer2_d1 = document . getElementById ( "observer2_d1" );
var $observer2_d2 = document . getElementById ( "observer2_d2" );
var $observer2_result = document . getElementById ( "observer2_result" );
var $d1_value = parseFloat ( $d1 . value );
var $d2_value = parseFloat ( $d2 . value );
if ( ! isNaN ( $d1_value ) && ! isNaN ( $d2_value )) {
//反馈数据
var $result_value = $d1_value + $d2_value ;
$result . innerText = $result_value ;
$observer1 . setAttribute ( "class" , "show observer1" );
$observer1_d1 . innerText = $d1_value ;
$observer1_d2 . innerText = $d2_value ;
$observer1_result . innerText = $result_value ;
$observer2 . setAttribute ( "class" , "show observer2" );
$observer2_d1 . innerText = $d1_value ;
$observer2_d2 . innerText = $d2_value ;
$observer2_result . innerText = $result_value ;
} else {
$result . innerText = "" ;
$observer1 . setAttribute ( "class" , "hide" );
$observer2 . setAttribute ( "class" , "hide" );
}
}
</ script >
< style >
. item {
margin : 10 px ;
}
. show {
display : block
}
. hide {
display : none
}
. observer1 {
background-color : dimgray ;
color : white ;
margin : 40 px ;
font-size : 20 px ;
padding : 40 px ;
}
. observer2 {
background-color : bisque ;
color : black ;
margin : 40 px ;
padding : 40 px ;
font-size : 20 px ;
}
</ style >
vue-version.html <!DOCTYPE html>
< html >
< head >
< script src = "js\petite-vue.iife.js" ></ script >
</ head >
< body >
< div id = "app" >
< h4 > 数据的双向绑定示例:加法器----Vue.js的实现</ h4 >
< div class = "item" >
< span > 数1:</ span >
< input type = "number" v-model = "d1" />
</ div >
< div class = "item" >
< span > 数2:</ span >
< input type = "number" v-model = "d2" />
</ div >
< div class = "item" >
< span > 结果:</ span >
< span > {{result}}</ span >
</ div >
< div v-if = "result" class = "observer1" >
< span > 用户操作:</ span > 数1:< span > {{d1}}</ span > ,数2:< span > {{d2}}</ span > ,结果是:< span > {{result}}</ span >
</ div >
< div v-if = "result" class = "observer2" >
< span > 也就是说:</ span >< span > {{d1}}</ span > +< span > {{d2}}</ span > =< span > {{result}}</ span >
</ div >
</ div >
</ body >
</ html >
< script >
PetiteVue . createApp (). mount ( "#app" )
new Vue ({
el : '#app' ,
data () {
return {
d1 : null ,
d2 : null ,
result : null
}
},
watch : {
d1 () {
this . onInput ();
},
d2 () {
this . onInput ();
}
},
methods : {
onInput () {
var $d1_value = parseFloat ( this . d1 );
var $d2_value = parseFloat ( this . d2 );
if ( ! isNaN ( $d1_value ) && ! isNaN ( $d2_value )) {
//反馈数据
this . result = $d1_value + $d2_value ;
} else {
this . result = null ;
}
}
}
})
</ script >
< style >
. item {
margin : 10 px ;
}
. show {
display : block
}
. hide {
display : none
}
. observer1 {
background-color : dimgray ;
color : white ;
margin : 40 px ;
font-size : 20 px ;
padding : 40 px ;
}
. observer2 {
background-color : bisque ;
color : black ;
margin : 40 px ;
padding : 40 px ;
font-size : 20 px ;
}
</ style >
vuetify-version.html <!DOCTYPE html>
< html >
< head >
< link href = "css/fonts.css" rel = "stylesheet" >
< link href = "css/materialdesignicons.min.css" rel = "stylesheet" >
< link href = "css/vuetify.min.css" rel = "stylesheet" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui" >
</ head >
< body >
< div id = "app" >
< v-app >
< v-main >
< v-card class = "mx-auto" max-width = "500" >
< v-toolbar color = "deep-purple accent-4" dark >
< v-toolbar-title > 数据的双向绑定示例:加法器----Vuetify的实现</ v-toolbar-title >
</ v-toolbar >
< v-card-text >
< v-text-field label = "数1:" :rules = "rules" hide-details = "auto" v-model = "d1" ></ v-text-field >
< v-text-field label = "数2:" :rules = "rules" hide-details = "auto" v-model = "d2" ></ v-text-field >
< v-text-field label = "结果:" v-model = "result" readonly ></ v-text-field >
</ v-card-text >
</ v-card >
</ v-main >
</ v-app >
</ div >
</ body >
</ html >
< script src = "js/vue.js" ></ script >
< script src = "js/vuetify.js" ></ script >
< script >
new Vue ({
el : '#app' ,
vuetify : new Vuetify (),
data () {
return {
d1 : null ,
d2 : null ,
result : null ,
rules : [
value => !! value || '必填项.'
],
}
},
watch : {
d1 () {
this . onInput ();
},
d2 () {
this . onInput ();
}
},
methods : {
onInput () {
var $d1_value = parseFloat ( this . d1 );
var $d2_value = parseFloat ( this . d2 );
if ( ! isNaN ( $d1_value ) && ! isNaN ( $d2_value )) {
//反馈数据
this . result = $d1_value + $d2_value ;
} else {
this . result = null ;
}
}
}
})
</ script >
< style >
. item {
margin : 10 px ;
}
. show {
display : block
}
. hide {
display : none
}
. observer1 {
background-color : dimgray ;
color : white ;
margin : 40 px ;
font-size : 20 px ;
padding : 40 px ;
}
. observer2 {
background-color : bisque ;
color : black ;
margin : 40 px ;
padding : 40 px ;
font-size : 20 px ;
}
</ style >