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 inputnode -vTo 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

  1. The Form is responsible for defining validation rules
  2. FormtItem is responsible for displaying error messages
  3. Input is responsible for bidirectional data binding
  4. 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

  1. Gets the rules for the current input box
  2. If the input box does not match the rule, an error message is displayed
  3. When the user enters something in the Input component, FormItem is notified to validate it
  4. 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

  1. Get the data model and validation rules in the props declaration
  2. When the FormItem component finishes mounting, notify the Form component to start caching the Form items that need validation
  3. Returns false if one of the cached form items is an errorpromise.all()To process)
  4. 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