Single file component
In many Vue projects, we use Vue.com Ponent to define global components, followed by new Vue({el: ‘#app ‘}) to specify a container element within each page.
This approach works well for many small to medium sized projects where JavaScript is only used to enforce specific views. But in more complex projects, or when your front end is entirely javascript-driven, the following disadvantages become obvious:
- Global definitions enforce that names in each Component must not be repeated
- String templateLack of syntax highlighting and use of ugly when HTML has multiple lines
\
- Not supporting CSS means that CSS is conspicuously missing when HTML and JavaScript are componentized
- There are no build steps limiting use to HTML and ES5 JavaScript, not preprocessors such as Pug (formerly Jade) and Babel
Single-file Components with a.vue file extension provide a solution to all of these problems, and build tools like Webpack or Browserify are also available.
Here is a simple example of a file called hello.vue:
Now we get
- Full syntax highlighting
- CommonJS module
- Component-scoped CSS
After reading the above, it is recommended to use the official Vue CLI 3 scaffolding to develop tools, and just follow the tips to quickly run a Vue project with.vue components,ES2015, Webpack, and hot overloading
Vue CLI3
The basic configuration
- Install Nodejs
- Ensure node.js8.9 or later
- Terminal input
node -v
To ensure that the installation is successful
- The installationTaobao Mirror source
npm install -g cnpm --registry=https://registry.npm.taobao.org
- Later NPM can be replaced by CNPM
- Install Vue Cli3 scaffolding
cnpm install -g @vue/cli
- Check whether the version is correct
vue --version
Rapid prototyping
Quick prototyping of individual *. Vue files using vue serve and vue build commands, but this requires an additional global extension to be installed first:
npm install -g @vue/cli-service-global
Copy the code
The downside of Vue Serve is that it requires global dependencies to be installed, which makes it inconsistent across machines. So this is only for rapid prototyping.
All you need is an app.vue file:
<template> <div> <h2> Hello world single page component </h2> </div> </template> <script> export default {} </script> <style> </style>Copy the code
Then run it in the same directory as the app. vue file:
vue serve
Copy the code
Startup effect:
Webpage effect:
However, this approach was limited to rapid prototyping, and ultimately the vue CLI3 was used to start the project
Create a project
vue create mysite
Copy the code
See the official website for details
The shopping cart
App.vue
<ul>
<li v-for="(item, index) in cartList" :key="index">
<h3>{{item.title}}</h3>
<p>Selections {{item. Price}}</p>
<button @click='addCart(index)'>Add a shopping cart</button>
</li>
</ul>
Copy the code
cartList: [
{
id:1.title:'Web Full Stack Development'.price:1999
},
{
id: 2.title: 'Python Full Stack Development'.price: 2999}].Copy the code
Create a new cart.vue shopping Cart component
<template>
<div>
<table border='1'>
<tr>
<th>#</th>
<th>course</th>
<th>The unit price</th>
<th>The number of</th>
<th>The price</th>
</tr>
<tr v-for="(c, index) in cart" :key="c.id" :class='{active:c.active}'>
<td>
<input type="checkbox" v-model='c.active'>
</td>
<td>{{c.title}}</td>
<td>{{c.price}}</td>
<td>
<button @click='subtract(index)'>-</button>
{{c.count}}
<button @click='add(index)'>+</button>
</td>
<td>Selections {{c.p rice * Arthur c. mount}}</td>
</tr>
<tr>
<td></td>
<td colspan="2">{{activeCount}}/{{count}}</td>
<td colspan="2">{{total}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "Cart".props: ['name'.'cart'].methods: {
subtract(i) {
let count = this.cart[i].count;
// if(count > 1){
// this.cart[i].count-=1
// }else{
// this.remove(i)
// }
count > 1 ? this.cart[i].count -= 1 : this.remove(i);
},
add(i) {
this.cart[i].count++;
},
remove(i) {
if (window.confirm('Make sure you want to delete')) {
this.cart.splice(i, 1); }}},data() {
return{}},created() {},
computed: {
activeCount() {
return this.cart.filter(v= > v.active).length;
},
count() {
return this.cart.length;
},
total() {
// let num = 0;
// this.cart.forEach(c => {
// if (c.active) {
// num += c.price * c.count
/ /}
// });
// return num;
return this.cart.reduce((sum, c) = > {
if (c.active) {
sum += c.price * c.count
}
return sum;
}, 0)}}}</script>
<style scoped>
.active {
color: red;
}
</style>
Copy the code
The mock data
Make a mock out of the webpack-dev-server and create vue.config.js to extend the webpack setting
Webpack official website introduction
module.exports = {
configureWebpack: {devServer: {// mock data
before(app,server){
app.get('/api/cartList'.(req,res) = >{
res.json([
{
id:1.title:'Web Full Stack Development'.price:1999
},
{
id: 2.title: 'Web Full Stack Development'.price: 2999}])})}}}}Copy the code
Visit http://localhost:8080/api/cartList to view the mock data
NPM install axios-s to fetch interface data using axios
created() {
axios.get('/api/cartList').then(res= >{
this.cartList = res.data
})
}
Copy the code
Use ES7’s async+await syntax
async created() {
// try-catch resolves async-aWIAT error handling
try {
const { data } = await axios.get('/cartList')
this.cartList = data;
} catch (error) {
console.log(error); }},Copy the code
Data persistence
Localstorage + vue listeners
If the component has no obvious parent-child relationship, use the central event bus for delivery
Each instance of Vue has a subscription/publish implementation using ON and ON and ON and EMIT
main.js
Vue.prototype.$bus = new Vue();
Copy the code
App.vue
methods: {
addCart(index) {
const good = this.cartList[index];
this.$bus.$emit('addGood',good); }}Copy the code
Cart.vue
data() {
return {
cart:JSON.parse(localStorage.getItem('cart') | | []}},// Array and object depth listening
watch: {
cart: {
handler(n, o) {
const total = n.reduce((total, c) = > {
total += c.count
return total;
}, 0)
localStorage.setItem('total', total);
localStorage.setItem('cart'.JSON.stringify(n));
this.$bus.$emit('add', total);
},
deep: true}},created() {
this.$bus.$on('addGood'.good= > {
const ret = this.cart.find(v= > v.id === good.id);
if (ret) { // The shopping cart already has data
ret.count += 1;
} else {
// The shopping cart has no data
this.cart.push({ ... good,count: 1.active: true}}}})),Copy the code
For more complex data transfer, you can use VUex, which will be discussed later in the course
The components
Component classification
- Common component
- Basic components, most UI components, such as form layout pop-ups, etc
- The business component
- It’s tied to demand, it’s reused, like a lottery, a shake, etc
- Page components
- Each page is a component V
Using third-party Components
For example, Vue’s most popular element, which is a typical generic component, performs NPM install Element-UI installation
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app'.render: h= > h(App)
});
Copy the code
In vue-CLI, you can install using the Vue Add Element
Be sure to submit your current work in advance before installation. Scaffolding will cover several files
Notice that the project has changed, open app. vue, CTRL + Z undo
You can use
in any component.
The common components of Element-UI on the official website are basically copied and pasted. I will not describe them here. We will use the library in the following projects
In terms of component design, the most important thing is to design the components ourselves. Now let’s imitate the form components provided by Element-UI and implement the form component M-Form by hand
Take a look at the element-UI form first
New FormElement. Vue
<template>
<div>
<h3>Element form</h3>
<el-form
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="Username" prop="name">
<el-input type="text" v-model="ruleForm.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="Confirm password" prop="pwd">
<el-input type="password" v-model="ruleForm.pwd" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">submit</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "FormElement".data() {
return {
ruleForm: {
name:' '.pwd:' '
},
rules: {name:[
{required:true.message:'Please enter a name'},
{min:6.max:10.message:'Please enter 6~10 user names'}].pwd: [{require:true.message:'Please enter your password'}].}}},methods: {
submitForm(name) {
this.$refs[name].validate(valid= >{
console.log(valid);
if(valid){
alert('Verified successful, ready to commit')}else{
alert('the error to submit');
return false; }})}},};</script>
Copy the code
Import the component in the app. vue component, mount it, and use
Component design
Form components, component layering
- The Form is responsible for defining validation rules
- FormtItem is responsible for displaying error messages
- Input is responsible for bidirectional data binding
- Share data internally using provide and Inject
Form controls implement two-way data binding
Input.vue
<template>
<div>
<input :type="type" @input="handleInput" :value="inputVal">
</div>
</template>
<script>
export default {
props: {
value: {
type: String.default: ""
},
type: {
type: String.default: "text"}},data() {
return {
// The rule for unidirectional data flow is that you cannot modify props within a component
inputVal: this.value
};
},
methods: {
handleInput(e) {
this.inputVal = e.target.value;
// Notifies the parent of an update to the value
this.$emit("input".this.inputVal); }}};</script>
<style scoped>
</style>
Copy the code
FormElement.vue
If type is not passed, the default value is described in the props for input. vue<m-input v-model="ruleForm.name"></m-input>
<m-input v-model="ruleForm.name" type='password'></m-input>
Copy the code
/ / data
data() {
return {
ruleForm: {
name: "".pwd: ""
},
rules: {
name: [{required: true.message: "Please enter a name" },
{ min: 6.max: 10.message: "Please enter 6~10 user names"}].pwd: [{ require: true.message: "Please enter your password"}}}; },Copy the code
FormItem
- Gets the rules for the current input box
- If the input box does not match the rule, an error message is displayed
- When the user enters something in the Input component, FormItem is notified to validate it
- Use async-Validator to perform validation
FormItem.vue
<template>
<div>
<label v-if="label">{{label}}</label>
<slot></slot>
<! -- Check error message -->
<p v-if="validateStatus=='error'" class="error">{{errorMessage}}</p>
</div>
</template>
<script>
import schema from "async-validator";
export default {
name: "FormItem".data() {
return {
validateStatus: "".errorMessage: ""
};
},
props: {
label: {
type: String.default: ""
},
prop: {
type: String}}};</script>
<style scoped>
.error {
color: red;
}
</style>
Copy the code
FormElement.vue
<m-form-item label="Username" prop="name">
<m-input v-model="ruleForm.name"></m-input>
</m-form-item>
<m-form-item label="Password" prop="pwd">
<m-input v-model="ruleForm.pwd" type="password"></m-input>
</m-form-item>
Copy the code
In this case, the web page is displayed normally but there is no verification rule. Add a verification rule
For example, to verify the user name, the entered user name must contain 6 to 10 characters
npm i asycn-validator -S
Copy the code
Input.vue
methods: {
handleInput(e) {
this.inputVal = e.target.value;
//....
// Notify the parent component to verify and pass in the value of the input field in real time
this.$parent.$emit("validate".this.inputVal); }}Copy the code
FormItem.vue
import schema from "async-validator";
export default {
name: "FormItem".data() {
return {
validateStatus: "".errorMessage: ""
};
},
methods: {
validate(value) {//value is the value of the current input field
// Validate the current item: depends on async-validate
let descriptor = {};
descriptor[this.prop] = this.form.rules[this.prop];
// const descriptor = { [this.prop]: this.form.rules[this.prop] };
const validator = new schema(descriptor);
let obj = {};
obj[this.prop] = value;
// let obj = {[this.prop]:this.form.model[this.prop]};
validator.validate(obj, errors= > {
if (errors) {
this.validateStatus = "error";
this.errorMessage = errors[0].message;
} else {
this.validateStatus = "";
this.errorMessage = ""; }}); }},created() {
// Listen for the validate event distributed to the child component's Input
this.$on("validate".this.validate);
},
// Inject the name to get the parent Form before we create it
inject: ["form"].props: {
label: {
type: String.default: ""
},
prop: {
type: String}}};Copy the code
Form
- Get the data model and validation rules in the props declaration
- When the FormItem component finishes mounting, notify the Form component to start caching the Form items that need validation
- Returns false if one of the cached form items is an error
promise.all()
To process) - Declare a validation method for the parent component method to call the validate() method
Form.vue
Get the data model and validation rules in the props declaration
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
name:'Form'./ / rely on
provide(){
return {
// We can get this.form.rules and this.form.rules in the child component by passing the instance of the form to the descendant
form: this}},props: {model: {type:Object.required:true
},
rules: {type:Object}}},</script>
Copy the code
When the FormItem component finishes mounting, notify the Form component to start caching the Form items that need validation
FormItem.vue
mounted() {
// When mounted to the form, an add event is sent
// A judgment must be made because the child of the Form component may not be FormItem
if (this.prop) {
// Notification caches form items
this.$parent.$emit("formItemAdd".this); }}Copy the code
Form.vue
created () {
// Cache the form items that need validation
this.fileds = []
this.$on('formItemAdd'.(item) = >{
this.fileds.push(item); })},Copy the code
The cached form items are processed uniformly, and if one of them is an error, false is returned.
Note: Because the first argument to the promise. all method is an array object that holds multiple Promise objects, you need to modify the validate method of the FormItem
FormItem.vue
validate() {
// Validate the current item: depends on async-validate
return new Promise(resolve= > {
const descriptor = { [this.prop]: this.form.rules[this.prop] };
const validator = new schema(descriptor);
validator.validate({[this.prop]:this.form.model[this.prop]}, errors= > {
if (errors) {
this.validateStatus = "error";
this.errorMessage = errors[0].message;
resolve(false);
} else {
this.validateStatus = "";
this.errorMessage = "";
resolve(true); }}); }); }Copy the code
Form.vue
methods: {
validate(callback) {
// If there is a single failure, it will fail.
// Change the validation of the formItem validate method to a Promise object and save the validated Boolean value
// Tasks hold multiple promise objects after validation
const tasks = this.fileds.map(item= >item.validate());
let ret = true;
// Handle multiple promise objects to validate, return false if there is one error,
Promise.all(tasks).then(results= >{
results.forEach(valid= >{
if(! valid){ ret =false; } }) callback(ret); })}},Copy the code
Testing:
<m-form :model="ruleForm" :rules="rules" ref="ruleForm2">
<m-form-item label="Username" prop="name">
<m-input v-model="ruleForm.name"></m-input>
</m-form-item>
<m-form-item label="Password" prop="pwd">
<m-input v-model="ruleForm.pwd" type="password"></m-input>
</m-form-item>
<m-form-item>
<m-button type="danger" @click="submitForm2('ruleForm2')">submit</m-button>
</m-form-item>
</m-form>
Copy the code
methods:{
submitForm2(name) {
this.$refs[name].validate(valid= >{
console.log(valid);
if(valid){
alert('Verification successful');
}else{
alert('Verification failed')}}); }}Copy the code