这篇文章中,咱们将讲解 Vue 实例的 Props 和 Methods,接着咱们又讲解了最多见的 Vue 模板语法,并经过实例的方式将这些模板语法都实践了一番,最后咱们讲解了 Vue 组件的组合,并完成了咱们的发表商品页面。html
欢迎阅读《从零到部署:用 Vue 和 Express 实现迷你全栈电商应用》系列:前端
若是您以为咱们写得还不错,记得 点赞 + 关注 + 评论 三连,鼓励咱们写出更好的教程💪
当咱们完成了商城应用的基本页面框架以后,咱们就能够开始考虑具体页面的内容了。首先咱们要考虑的就是数据的来源,即添加商品页面。有了添加商品的入口,咱们就能够展现商品列表,获取商品详情,甚至是修改商品信息。vue
不过在此以前,咱们打算先复习一下 Vue 的一些重要知识点。若是你已经很熟悉了,能够直接跳到下面实现 ProductForm.vue 的代码部分。segmentfault
props
是 Vue 进行组件之间传参的形式。好比咱们有如两个组件 New.vue
和 ProductForm.vue
,在 New.vue
组件中须要使用到 ProductForm.vue
组件。其中 New.vue
组件是用来建立商品的,它的代码大体是这样的:后端
import ProductForm from '@/components/ProductForm.vue'; <ProductForm :manufacturers="manufacturers" />
它须要给 ProductForm.vue
组件传递一个 manufacturers
属性,以确保咱们在建立商品时,能够选择这个商品所属的制造商,接着咱们就能够在 ProductForm.vue
中的 props
中取到这个 manufacturers
属性。ProductForm.vue
的代码大体是这样的:数组
<template> <!-- 模板部分 --> </template> <script> export default { props: ['manufacturers'], } </script>
能够看到,咱们在 ProductForm.vue
的 script
部分导出的对象里面找到 props
属性,而后取到 manufacturers
属性。浏览器
而后是 methods
,methods
是用来定义在组件中会用到的一些方法,若是说咱们前面提到的 data
,是从数据从逻辑层(JS)向视图层(Html)流动的话,那么这里的 methods
就是视图层触发事件,如 click、submit等,反过来修改逻辑层的数据的方法,methods
使得数据能够双向流动。app
让咱们在完善一下咱们的 ProductForm.vue
,看一下 Methods 在 Vue 中是如何运做的:框架
<template> <form @submit.prevent="saveProduct"> <!-- 其余表单,如 input 等 --> <div class="form-group new-button"> <button class="button">Add Product</button> </div> </form> </template> <script> export default { data: { isSaved: false }, props: ['manufacturers'], methods: { saveProduct() { this.isSaved = true; // 完成一些保存建立商品的逻辑 ... } } } </script>
能够看到,咱们能够经过在 template
(视图层)经过点击提交按钮,发起表单提交事件,进而调用在 script
中定义在 methods
属性中的 saveProduct
方法,这个方法能够进一步修改定义在定义在 data
属性中的数据;甚至若是父组件 New.vue
传递了方法(以 props
的形式)给 ProductForm.vue
组件,咱们可在 saveProduct
调用这个传递下来的方法,进而能够影响到父组件 New.vue
中的数据。咱们将在后面的正式实现 ProductForm
组件时讲解到它。工具
接下来咱们再来谈一谈 v-bind 和 v-on 。
在 Vue 中,咱们经过 v-on
的方式接管了以前在 HTML 中 onEvent
:
好比以前咱们在 HTML 中的写法是这样的:
<div onclick="alert('I love tuture')"> Hello Tuture </div>
如今在 Vue 的模板语法中咱们须要写出这样:
<div v-on:click="alert('I love tuture')"> Hello Tuture </div>
相似的 onEvent
都要改为 v-on:Event
。而后这样写显得比较冗余,因此 Vue 支持简化写法,用 @
替换 v-on:
部分,咱们就能够写出这样:
<div @click="alert('I love tuture')"> Hello Tuture </div>
调用事件以后咱们通常有一些这样的操做,好比禁用浏览器默认行为,而后本身去处理事件,获取后端数据,之前咱们会这样写:
<div onclick="saveProduct()"> Hello Tuture <script> var saveProduct = function (e) { e.preventDefault(); // do something you like } </script>
可是这样写又显得特别繁琐了,Vue 也以为这样能够简化,因而咱们直接将这些禁止默认行为的调用做为绑定事件的属性来进行处理,因而乎在 Vue 中咱们能够写出这样:
<template> <div @click.prevent="saveProduct"> Hello Tuture </div> </template> <script> export default { methods: { saveProduct() { // do something you like } } } </script>
不知道看了上面的长文,你有没有一点晕,无论你晕不晕,我是得喝口水缓一下。 - v -
咱们已经看到在 Vue 模板中咱们可使用以下的功能:
{{}}
插值语法将 data
渲染到 HTML 元素内容中v-on
或者简化写法 @
,等用来取代 HTML 的事件绑定有了上面的功能,咱们可让 HTML 动起来了,可是还缺点什么,好比咱们的 HTML 属性,如 id
、class
等,是否是也能动态的获取变化值,你还别说,还真的能够,Vue 模板语法为咱们提供了 v-bind
用于动态绑定属性值,咱们来看个例子:
<template> <option v-bind:id="_id" v-bind:value="value" /> </template> <script> export default { data: { _id: '1', value: "Xiaomi" }, } </script>
能够看到,咱们在 script
中导出的对象属性 data
中,定义了 _id
和 value
值,而后咱们经过在 <template>
模板中使用 v-bind
语法动态的给 option
标签的 id
和 value
属性赋值,最后的结果看起来是这样的:
<option id="1" value="Xiaomi" />
固然,当须要绑定的属性多了,每次都写 v-bind
显得至关繁琐,因此 Vue 为咱们提供了 v-bind
的简洁语法 :
,即咱们以前的绑定语法从 v-bind:id="_id"
变成了 :id="_id"
。
上面的代码用简洁语法改写以下:
<template> <option :id="_id" :value="value" /> </template> <script> export default { data: { _id: '1', value: "Xiaomi" }, } </script>
前面咱们提到经过 {{}}
插值语法渲染来自 data
的数据实现了逻辑层向视图层的数据流动,经过 methods
在视图层操做逻辑层的数据,实现了视图层的数据向逻辑层的数据流动,从而达到了双向绑定,当咱们的应用愈来愈复杂,咱们会发现这样的数据双向流动会愈来愈频繁,并且粒度也会大小不一,有不少单纯修改某个值的方法调用就会显得特别繁杂,所以 Vue 经过提供 v-model
进行了视图层和逻辑层的双向绑定,让咱们来看个例子:
<template> <!-- 其余代码 ... --> <input type="text" placeholder="Name" v-model="name" /> <!-- 其余代码 ... --> </template> <script> export default { data: { name: 'ProductForm' }, } </script>
这里咱们经过申明 v-mode
将此 input 的值和咱们在 Vue 实例中的 model
的 name
属性进行了双向绑定,即当 data 中的 name
发生变化,input 的值也会跟着变化,当 input 的值发生变化,咱们 data 中的 name
的值也会被修改,这一切都是自动发生的,不须要咱们额外的添加 methods
里面的方法调用来手动修改。
好了,Vue 替咱们接管了 HTML 元素属性值、事件处理、元素内容,这些都还只属于原来 HTML 的部分,它更强大的一点就是将 JS 的功能引入了模板语法中,使得咱们能够实现相似循环,条件选择操做等功能。
接下来咱们先来看一下 Vue 为咱们提供的 “循环” 模板语法, 它使得咱们能够快速渲染大量具备类似结构的数据,好比渲染一个数组的数据,生成一个 HTML 元素列表,这在咱们平时看到的新闻 App 里面很常见,咱们浏览新闻时,发现其实每条新闻的结构都很类似,而且有不少条新闻(可能多大几百上千条),若是每一条咱们都手动写 HTML 代码的话,无疑显得至关繁琐,而且数据一多,咱们手动就显得无能为力了,而 Vue 为咱们提供的 “循环” 模板语法,使得咱们能够经过很是简单的写法就能够渲染大量数据,咱们来看个例子:
<!-- manufacturers = [ { _id: 1, name: 'Apple' }, { _id: 2, name: 'Xiaomi' } ] model = { _id: 1, name: 'Apple' } --> <template v-for="manufacturer in manufacturers"> <option :value="manufacturer._id" :selected="manufacturer._id == model._id">{{manufacturer.name}}</option> </template>
最后渲染的结果为:
<option value="1" selected="true">Apple</option> <option value="2" selected="false">Xiaomi</option>
注意到,若是咱们在写 “循环” 语法时,使用了一个额外的标签 template
来包裹咱们须要渲染的 HTML 元素,这也是 Vue 推荐的写法;咱们在 template
标签的属性上添加 v-for
而后给它赋值 "manufacturer in manufacturers"
,经过这样的形式进行列表数据的遍历,每次从 manufacturers
中取一个元素,并赋值给 manufacturer
,而后咱们就能够在 option
标签中使用 manufacturer
和咱们定义的 model
进行比较。
由于咱们的 model._id
为 1
,它和 manufacturers
数组的第一项元素的 _id
一致,因此咱们返回的两个 option
标签,第一个 selected
属性为 true
,第二个为 false
。
上面的讲述了循环是如何在 Vue 中使用的,下面咱们来看一看条件语法是如何在 Vue 中使用的:
<span v-if="isEditing">Update Product</span> <span v-else>Add Product</span> <script> export default { data: { isEditing: false }, } </script>
咱们能够看到,经过在标签上加 v-if
并后面紧跟加 v-else
的标签咱们能够判断最终渲染的标签,好比咱们这里 isEditing
为 false
,那么咱们最终渲染的结果为:
<span>Add Product</span>
固然你能够添加诸如 v-else-if
的标签来作多重判断。
提示
这里的带
v-if
、v-else-if
或v-else
的标签须要依次紧跟着前面的标签,不能在这些带条件属性的标签中插入其余不带条件的标签,好比下面这段代码就是错误的:<span v-if="isEditing">Update Product</span> <span>我是错误插入的标签</span> <span v-else>Add Product</span> <script> export default { data: { isEditing: false }, } </script>
讲解完 Vue 的基础知识以后,咱们立刻将全部的知识运用起来,来编写咱们的 ProductForm.vue
组件,它用来添加或者更新商品的信息。
咱们在 src/components
中建立 ProductForm.vue
表单组件,代码以下:
<template> <form @submit.prevent="saveProduct"> <div class="col-lg-5 col-md-5 col-sm-12 col-xs-12"> <div class="form-group"> <label>Name</label> <input type="text" placeholder="Name" v-model="model.name" name="name" class="form-control" /> </div> <div class="form-group"> <label>Price</label> <input type="number" class="form-control" placeholder="Price" v-model="model.price" name="price" /> </div> <div class="form-group"> <label>Manufacturer</label> <select type="text" class="form-control" v-model="model.manufacturer" name="manufacturer"> <template v-for="manufacturer in manufacturers"> <option :value="manufacturer._id" :selected="manufacturer._id == (model.manufacturer && model.manufacturer._id)">{{manufacturer.name}}</option> </template> </select> </div> </div> <div class="col-lg-4 col-md-4 col-sm-12 col-xs-12"> <div class="form-group"> <label>Image</label> <input type="text" lass="form-control" placeholder="Image" v-model="model.image" name="image" class="form-control" /> </div> <div class="form-group"> <label>Description</label> <textarea class="form-control" placeholder="Description" rows="5" v-model="model.description" name="description" ></textarea> </div> <div class="form-group new-button"> <button class="button"> <i class="fa fa-pencil"></i> <!-- Conditional rendering for input text --> <span v-if="isEditing">Update Product</span> <span v-else>Add Product</span> </button> </div> </div> </form> </template> <script> export default { props: ['model', 'manufacturers', 'isEditing'], methods: { saveProduct() { this.$emit('save-product', this.model) } } } </script>
这段代码看起来很长,你可能被吓到了,让咱们一段一段来拆解它。
这里咱们的 props
接收来自父组件的三个参数:model
、manufacturers
、isEditing
。
而后咱们定义了一个 saveProduct
方法,就是当用户填写完商品信息的表单以后,点击提交按钮会触发的方法,在 saveProduct
内部,咱们调用了父组件的 save-product
方法,并把修改后的 this.model
变量内容传给父组件。因此这里咱们还能够看到,methods
不只可使得数据能够双向流动,并且还能够在子组件反向操做父组件的内容,使得数据还能够上下流动。
接下来咱们再来谈一谈 template
里面发生的事情。
能够看到 template
里面就是一个表单,这个表单定义了一个 submit
事件,而且使用了禁用默认事件的简洁写法 @submit.prevent
。 这个事件触发会调用咱们上面提到的 saveProduct
方法。
接着咱们定义了好几个 class
为 form-group
的元素块,每一个块表明咱们建立商品所须要填写的相关信息,咱们注意到,前两个 form-group
使用 v-model
双向绑定语法分别绑定了 model
的 name
和 price
属性。
第三个 form-group
咱们首先在 select
标签中使用 v-model
双向绑定了 model.manufacturer
,表示咱们在视图里面进行选择时,会修改对应的 model.manufacturer
属性。
接着咱们对 manufacturers
进行循环遍历,构造多个 option
标签选项,而后使用了属性绑定语法的简洁写法绑定了 option
的 value
和 selected
属性,value
属性赋值为 manufacturer._id
,selected
属性会进行判断,model.manufacturer && model.manufacturer._id
表示首先检验 model
的 manufacturer
属性是否存在,正常状况下它应该是一个对象,若是 model.manufacturer
属性存在,那么获取 model.manufacturer._id
,而后用获取到的这个 model.manufacturer._id
和 manufacturer._id
进行比较,若是一致,那么 selected
属性为 true
,不一致就为 false
。
而后咱们来看一下第二段 form-group
,也就是第 4-6 个 form-group
。
能够看到前两个 form-group
使用 v-model
双向绑定了 model.image
和 model.description
,表示当用户上传了商品图片和描述以后,对应的 model.image
就会变成用户上传的商品图片,model.description
就会变成用户撰写的商品描述。
最后一个 form-group
咱们使用了条件选择语法,判断 isEditing
,来渲染不一样的按钮文案。
编写完上面的表单以后,咱们在 New.vue
中引入咱们建立的表单组件:
<template> <product-form @save-product="addProduct" :model="model" :manufacturers="manufacturers" > </product-form> </template> <script> import ProductForm from '@/components/products/ProductForm.vue'; export default { data() { return { model: {}, manufacturers: [ { _id: 'sam', name: 'Samsung', }, { _id: 'apple', name: 'Apple', }, ], }; }, methods: { addProduct(model) { console.log('model', model); }, }, components: { 'product-form': ProductForm } } </script>
当一个组件要在模板语法中使用另一个组件时,须要申明此组件,即在组件的 components
属性中申明要使用的组件,好比咱们上面使用名为 'product-form'
的名称来申明使用 ProductForm
组件,这样在 template
中咱们就能够以 <product-form />
形式使用导入的表单组件。
同时咱们在组件的 data
中定义了 model
和 manufacturers
以及在 methods
中定义了 addProduct
方法,并将它们以属性绑定 :model="model"
、:manufacturers="manufacturers"
和事件绑定 @save-product="saveProduct"
的方式传递给表单组件。
当保存上面编写的代码以后,咱们打开浏览器,点击导航连接 Admin
,而后点击子导航连接 New Products
,切换到咱们的 New.vue
添加商品页面,咱们能够看到以下的效果:
到如今为止,咱们已经了解了 Vue 的基础部分,包括:
掌握了这些知识后,咱们已经能够实现不少前端的功能,完成一些简单的 Vue 应用了。可是若是要完成数据逻辑复杂的大型应用,目前学到的知识就力不从心了。可是不要紧,咱们将在后面学习 Vuex 这一前端状态管理工具,有了 Vuex 的加持,咱们就能用 Vuex 写出任意复杂的应用了。
想要学习更多精彩的实战技术教程?来 图雀社区逛逛吧。