1. Project address

  • Vue front-end Github address
  • Github address of the node. js backend
  • Experience address – question bank system

2. Environment construction

  • git clone [resposibility](Resporesponsibility is the directory name, remember to replace)
  • cd [resposibility](Resporesponsibility is the directory name, remember to replace)
  • npm install(Install dependencies, some cannot install, remember to switch NPM sourceNot πŸ€” point me)
  • npm run dev(Enter developer mode)
  • npm run build(Packaging static resources)

3. Infrastructure construction

5. 1. Smart refrigerator

The infrastructure is built using open source librariesOpen source address πŸ€”When I saw many stars, I choseBehind their actual writing business module, found that the pit is really much ah. The most common reason is that the project template has not been updated for a long time, resulting in the version of the dependency module is too low, and the dependencies are not compatible with each other, even thenwebpackIs 3.0. Through a large number ofgithub issueSearch, Google English search, gradually improved down.


2. Step in the pits and learn

2.1 Project architecture analysis





2.2 MOCK
  • (1)main.jsThe introduction ofmock:
import '.. /mock';
Copy the code
  • (2) configurationmockinformation
import Mock from 'mockjs';

// Mock the API through mock.mock ()
Mock.mock('/api/goodslist'.'get', {
    status: 200.message: 'Data list obtained successfully'.'data|5-10': [{/ / 'id | + 1:0, / / simulate the growth of id
            id: '@increment(1)'.name: '@ cword (2, 5)'.price: '@ natural (2, 10)'.count: '@ natural (100999)'.img: '@dataImage(25x25)'}}]);Copy the code
  • (3) useaxiosThe local requestmockdata

Axios’ baserURl needs to be /, and then the normal request is

this.$ajax({
      url: '/api/goodslist'.method: 'get',
    })
    .then(data= > {
      console.log(data, 'from mock')})Copy the code

The results of



2.3 webpack
  • (1) Node.jsCore modulepath

    path.__dirnameIndicates the absolute address of the directory where the current file resides.path.resolve()andpath.join()The difference between

  • (2) HtmlWebpackPluginThe plug-in

    webpackEntrance to the fileentryformain.js** Set the publicPath to “**” to ensure that static resources are properly located when deployed on the server. Otherwise, the address path does not match.

    throughHtmlWebpackPluginPlug-ins can automatically inject packaged static resources into already written resourcesindex.htmlFile. So that becomes the originalindex.htmlThe introduction ofhead,link,body,scriptThe way. Opening through file is incomplete and needs to be usednode.jsStatic resource loading method to obtain static resources. namelyapp.use('/', express.static(path.resolve(__dirname, './dist')))In a way.

  • (3) BundleAnalyzerPluginThe plug-in

    This plug-in is mainly used to analyze static resources after packagingsizeSituation.



Configuration is as follows

  plugins: [
  new VueLoaderPlugin(),
  new HtmlWebpackPlugin({
    template: path.resolve(__dirname, './public/index.html')}),new BundleAnalyzerPlugin({
    analyzerHost: '127.0.0.1'.analyzerPort: '7000'})].Copy the code

At present, I feel that the size of each module should be evenly distributed. For example, large modules should be separated as far as possible, such as the previous Element-UI and Vue modules. After the introduction of packaging resources are very large, it takes a long time for my 1G2 core server to load pages for the first time. So I looked it up, read the Webpack documentation, and introduced externals;

  • (4) externalsConfiguration (Packaged resource optimization)

Instead of packing imported packages into the bundle, obtain these external dependencies at runtime. In terms of optimization, try not to introduce a whole package because of a certain function. If you need this function, just study the code of the NPM package, and then deduct the functions you need and use them directly in the project. Of course, many Huawei employees do the same, but MIT agreement is required. Prototype link, this reference, execution context, etc. This will help you absorb the source code and then modify it to your own project.

<body>
      <! -- import Vue before Element -->
      <script src="https://unpkg.com/vue/dist/vue.js"></script>
      <! -- import JavaScript -->
      <script src="https://unpkg.com/element-ui/lib/index.js"></script>

      <script>
        let metaEl = document.querySelector('meta[name="viewport"]');
        let dpr = window.devicePixelRatio;
        let scale = 1 / dpr;

        metaEl.setAttribute('content'.'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=yes');  
          

      </script>
  
      <div id="app"></div>
</body>
Copy the code

Exernals are configured as follows

  externals: {
    vue: 'Vue'.'element-ui':'ELEMENT'
  },
Copy the code
  • (5) LoadersManagement resources

When we mix in resources such as images, fonts, styles, etc., we can use WebPack to import any other type of file through loader or the built-in Asset Modules. Webpack determines which files to look for based on regular expressions and feeds them to the specified loader.

module: {
    rules: [{test: /\.css$/,
        use: [
          'vue-style-loader'.'css-loader'],}, {test: /\.scss$/,
        use: [
          'vue-style-loader'.'css-loader'.'sass-loader'],}, {test: /\.sass$/,
        use: [
          'vue-style-loader'.'css-loader'.'sass-loader? indentedSyntax'],}, {test: /\.vue$/,
        loader: 'vue-loader'.options: {
          loaders: {
            'scss': [
              'vue-style-loader'.'css-loader'.'sass-loader'].'sass': [
              'vue-style-loader'.'css-loader'.'sass-loader? indentedSyntax'}}}, {test: /\.js$/,
        loader: 'eslint-loader'.enforce: "pre".include: [path.resolve(__dirname, 'src')].// Specify the directory to check
        options: {
          formatter: require('eslint-friendly-formatter') // Specify a format for error reporting
        },
        exclude: /node_modules/ 
      },

      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'file-loader'.options: {
          name: '[name].[ext]? [hash]'}}, {test: /\.(ttf|eot|svg|woff|woff2)$/,
        loader: 'url-loader'}},],Copy the code
  • (6) configurationwebpack-dev-serverHot update

The configuration is as follows:

 devServer: {
    contentBase: './dist',},Copy the code

Package. The json for the script

 "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot".Copy the code

The configuration tells webpack-dev-server to serve files in the dist directory to localhost:8080. (Serve, use resources as accessible files for the server)

Webpack-dev-server provides bundle files for the service from the directory defined in output.path, i.e., The file will be accessible through http://[devserver.host]:[devserver.port]/[output.publicpath]/[output.filename].

Webpack-dev-server is not written to any output files after compilation. Instead, you keep the bundle files in memory and then serve them to the server as if they were real files mounted on the server root path. If your page wants to find the bundle files in a different path, you can change this using the publicPath option in the Dev Server configuration.

3. Learn from the best

A good architecture takes vuE-cli3, then uses chainWebpack in vue.config.js, and then introduces the webpack.dev.js and webpack.pro.js environment modules. Of course, there is a common section between the two development modules, so there is the webpack.common.js module for dev and Pro environments. Then, the proxy server of the front-end local proxy continues to be abstracted. The configuration information, such as the proxy server address and port, is extracted separately and used as JSON. Json, as a module supported by WebPack natively, imports and exports directly without using module.exports at all. Then there are the webpack operations: loader, plugins, Performance, and so on.


4. Module construction

5. Business direction

6. Performance optimization

7. Package the deployment

8. Tool methods

NPM source switching

  • NRM provides the address of the next package. You can use NRM to switch the download source
  • NRM ls displays the address.
  • NPM is a package management tool that can be used to install packages.
  • NPM I CNPM -g downloads the CNPM package manager

If your company has its own NPM project source, be sure to ask your colleagues for the specific modification. NRMP address.

9. Function related (just the record when writing the project, the language has not prevented clear πŸ‘€)

1. The user submits the answer

The id of the answer submitted by the user is stored during login and obtained from the answer submitted page. When the user clicks the next question, the user information needs to be cached locally to prevent the user information from being lost. When a user accidentally refreshes a page, the echo interface can be invoked.

2. Dynamic route forwarding

As a reminder, when routing parameters are used, such as navigating from /user/foo to /user/bar, the original component instance is reused. Because both routes render the same component, reuse is more efficient than destroying and recreating. However, this also means that the component’s lifecycle hooks are no longer called. The reusable component is Summary

You can simply watch the $route object

const User = {
  template: '... '.watch: {
    $route(to, from) {
      // Respond to routing changes...}}}Copy the code

Or use the beforeRouteUpdate navigation guard introduced in 2.2:

@/components/summary/index.vue

  beforeRouteUpdate(to, from, next) {
    this.renderData(to.params.id, 2)
    next();
  },
Copy the code

3. The introduction of @include@mixin

@/common/z-input/index.vue

      $success: #586AEA;
    $warning: #ffcc00;
    $error: #cc3300;

    @mixin map-radio($color) {
        display: flex;
            justify-content: center;
            align-items: center;
            border: 1px solid $color;

            .pitch {
                display: inline-block;
                width: 6px;
                height: 6px;
                border-radius: 50%;
                background: $color;
                text-align: center; }}.success.radio {
            @include map-radio($success)
        }
        .warning.radio {
            @include map-radio( $warning)
        }
        .error.radio {
            @include map-radio($error)}Copy the code

10. Analysis of project design ideas

main.js:

  • The script
    1. Extend the functionality of the Vue constructor with prototypes:axiosencapsulation./utils/request.
    1. Add vUE bucket vue-Router and vuex as arguments to new vue (), and vue instantiate (β–Ά)What does instantiating the internal VUE do) will compile into the render functionThe home page component that matches vue-routerhome/index.vueCompile it into the Render function and mount it in the element with the ID App in the introduced app.vue.
    1. importThe introduction ofelement, vue.use (element)❓ Use element-UI components, import to introduce common SCSS styles,The import to introduce./permission.js(β–ΆVue component global routing hook) ❓ how the mechanisms introduced and the implementation within vUE extend the functionality of vUE instances.

Internal render

permission.js

Permission.js defines global routing hooks. Obtain the token stored in the local cookie through the tool function; If the token exists, the user accesses the login page, redirects path:/, and permits access to other pages. If no token exists and the path to be accessed is not in the whitelist, redirect to the login page. If a whitelist exists, the system permits the device.

import router from './router/index.js';
// import store from './store/index.js';
import tokenInstance from './utils/auth.js';  // Obtain the token from the cookie

const whiteList = ['/login'.'/'.'/sign'.'/register'];   // Route whitelist

router.beforeEach((to, from, next) = > { 
  const hasToken = tokenInstance.getToken();
 
  if(hasToken) {
    if(to.path == '/login') {
      next({ path: '/'})}else{ next(); }}else{ whiteList.indexOf(to.path) ! = = -1 ? next() : next({ path: '/login'})}})Copy the code

Technical analysis is involved in the project

Attempted to modify the value of a calculated property in a child component

The parent component
 /** * Clear the selected effect */
clearSelect() {
  this.render.choose.forEach((item,index) = > {
      let target = this.$refs['xInput' + index][0];
      target.innerState = ""})}Copy the code

Child components

computed: {
    innerState() {
        console.log(this.value, this.reply, 'hi')
        if(this.value === this.reply) {
            return 'success'}}}Copy the code

Error:

The solution

Because of mycomputedDo not rely ondataAttributes, so ❓Even if set estimation is set, it does not fire responsivelySo stop using computed for a while and usemethods. Well πŸ€” is a bit of a stretch, because computed relies on the props property, and props is also responsive. But the props specification does not support modification.

Continuous component transmission

When a component passes a value, the initial value defined by the parent component and the value changed by Ajax or other means are passed to the child component. If the sub component does not render the props value it receives, but instead assigns the props value to an attribute value in data, then the data cannot be rendered. ❓The reason is that the properties of data and props are initialized once and do not listen for changes to the values passed inYou may ask, why directly in<template></template>Templates can dynamically listen for props changes and render them. The reason:πŸ˜‚ and so on the back of the need to study. The solution is to use watch to listen for changes in the props value.

Save the props values locally

All prop forms a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the state of the parent component from the child, which can make the data flow of your application difficult to understand.

Additionally, every time the parent component changes, all prop in the child component will be refreshed to the latest value. This means that you should not change a prop inside a child component. If you do, Vue will issue a warning in the browser console.

There are two common cases of trying to change a prop:

  1. This prop is used to pass an initial value; This child component next wants to use it as a local prop data. In this case, it is best to define a local data Property and use this prop as its initial value:
props: ['initialCounter'].data: function () {
  return {
    counter: this.initialCounter
  }
}
Copy the code
  1. This prop is passed in as a raw value and needs to be converted. In this case, it is best to use the value of this prop to define a calculated property:
props: ['size'].computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}
Copy the code

Note that objects and arrays are passed in by reference in JavaScript, so for a prop of an array or object type, changing the object or array itself in a child component will affect the state of the parent component.

Select && refresh or jump route switch, select item display && question answer judgment

Results show


Logic instructions

Three parameters are extracted: solutionsArray Replys (user selection) “String Collection” value: “string”(x-input component value) When selected, compare the user’s selection with value, and return the “success”class in computed; When the user clicks submit, the interface is requested with the answer, and replys and Solutions compare: 1. Completely equal 2. Not equal 2.1 Partial Match 2.2 Partial Mismatch 2.3 Complete mismatch; And then matchList with value.