preface

This article describes some personal experience and thoughts of using Vue development project since graduation internship and formal work for one year. It is just a personal summary. If there is any unreasonable place, please make fun of it. The following is the outline of this article.

1.Vue project construction

1.1 From VueCli2 to VueCli3

At the beginning of my internship, Vue scaffolding was Vue 2 version, and the webpack configuration I learned was ALSO Cli2. Later, the company used Cli3, so there was a process of learning and adaptation. The differences between VueCli2 and VueCli3 are as follows:

  • Create a project

3.0: Vue Create. 2.0: Vue Init Webpack

  • Start the project

3.0 Start NPM run serve 2.0 Start NPM run dev

  • Project Configuration Approach

The 3.0 project structure is simpler than 2.0. Without build and Confilg files, you can create vue. Config. js files at the same level as package.json. Configure. The common configurations are as follows:

// Vue.config. js basic configuration method
module.exports = {
  // The base path for project deployment
  // We assume by default that your application will be deployed at the root of the domain name,
  / / such as https://www.my-app.com/
  // If your application is deployed under a subpath, you need to be here
  // Specify the subpath. For example, if your application is deployed in
  // https://www.foobar.com/my-app/
  // Then change the value to '/my-app/'
  // The base path baseURL is obsolete
  publicPath: '/'.// The directory of files built when the project is packaged, the same usage as the output.path of webpack itself
  outputDir: 'dist'.// Static resource directory (js, CSS, img, fonts)
  assetsDir: 'assets'.// eslint-loader whether to check for save, set to true to warn on the command line if compilation is not standard, or set to error to warn and compile fails
  lintOnSave: true.// Adjust the internal WebPack configuration. Refer to https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/webpack.md
  chainWebpack: () = > {},
  configureWebpack: () = > {},
  
  / / the vue - loader configuration items at https://vue-loader.vuejs.org/en/options.html
   vueLoader: {},
  
  The default value is true. If the sourceMap file in the production environment is not needed, set it to false to speed up the construction of the production environment
  productionSourceMap: true.// CSS related configurations
  css: {
   // Whether to use CSS ExtractTextPlugin, using independent style file load, do not use 
   extract: true.// If a style map is being built, false will speed up the build
   sourceMap: false.// CSS default configuration item
   loaderOptions: {},
   // Enable CSS modules for all CSS/pre-processor files.
   // This option does not affect '*.vue' files
   modules: false
  },
  
  // Use 'thread-loader' for Babel and TypeScript in production
  // It is enabled by default on multi-core machines.
  parallel: require('os').cpus().length > 1./ / whether to enable the DLL's https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#dll-mode
  dll: false.See HTTP: / / https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa / / PWA plug-in configuration
  pwa: {},
  
  // webpack-dev-server configuration
  devServer: {
   open: process.platform === 'darwin'.host: '0.0.0.0'.// If it is a real machine test, use this IP
   port: 1234.https: false.hotOnly: false.proxy: null.// Set the proxy
   // proxy: {
   // '/api': {
   // target: '
      
       ',
      
   // ws: true,
   // changOrigin: true
   / /}
   // },
   before: app= >{}},// Third-party plug-in configuration
  pluginOptions: {
   // ...}}Copy the code

1.2 Axios Secondary Encapsulation

Axios secondary encapsulation aims at three main aspects:

  • Request interception processing of the interface (configuration processing, interception of repeated requests)
  • Response interception processing of the interface
  • Package reuse of API methods

1.2.1 Interface Request Interception processing

1.2.1.1 Configurable items

When configuring interface request interception, you can flexibly configure the following parameters.

parameter meaning example
url The server URL used for the request url: ‘/user’
method The method used to create the request method: ‘get’
baseURL Automatically addurlUp front, unlessurlIs an absolute URL, by setting onebaseURLIt is easy to pass relative urls to axios instance methods baseURL: ‘some-domain.com/api/’
transformRequest Allows request data to be modified before being sent to the server

// Only for ‘PUT’, ‘POST’, and ‘PATCH’ request methods

// The function in the following array must return a string, either an ArrayBuffer, or a Stream
transformRequest: [function (data) {

// Perform any conversion on data
   return data;

}].
headers The custom request header to be sent  headers: {‘X-Requested-With’: ‘XMLHttpRequest’},
params The URL parameter to be sent with the request params: {

   ID: 12345

},
paramsSerializer To be responsible for theparamsThe serialization function (LLDB.www.npmjs.com/package/qs.Api.jquery.com/jquery.para…) paramsSerializer: function(params) {

   return Qs.stringify(params, {arrayFormat: ‘brackets’})

 }
data  dataIs the data sent as the body of the request

Only applies to these request methods ‘PUT’, ‘POST’, and ‘PATCH’

In no SettingstransformRequestMust be one of the following types:

– string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams

– Browser specific: FormData, File, Blob

– Dedicated to Node: Stream
data: {

   firstName: ‘Fred’

 }
timeout Specifies the number of milliseconds in which the request timed out (0 means no timeout) timeout: 1000
adapter Allow custom processing of requests to make testing easier, return a promise and apply a valid response adapter: function (config) {

/ *… * /

 },
auth Indicates that HTTP base authentication should be used and provides credentials, which will set oneAuthorizationHeader, overwrite existing arbitrary useheadersThe Settings are customizedAuthorizationhead auth: {

   username: ‘janedoe’,

   password: ‘s00pers3cret’

 },
responseType The data type of the server response can be ‘ArrayBuffer ‘, ‘blob’, ‘document’,’ JSON ‘, ‘text’, ‘stream’. ResponseType: ‘json’, // default
xsrfCookieName The name of the cookie used as the value of the XSRF token xsrfCookieName: ‘XSRF-TOKEN’
xsrfHeaderName The name of the HTTP header that carries the value of the XSRF token XsrfHeaderName: ‘x-xsrf-token ‘, // default
onUploadProgress Allows progress events to be processed for upload onUploadProgress: function (progressEvent) {

// Handling of native progress events

 },
onDownloadProgress Allows progress events to be processed for downloads  onDownloadProgress: function (progressEvent) {

// Handling of native progress events

 },
maxContentLength Defines the maximum size allowed for response content maxContentLength: 2000
validateStatus Define a resolve or Reject PROMISE for a given HTTP response status code. ifvalidateStatusreturntrue(Or set tonullundefined), promise will be resolved; Otherwise, the promise will be rejecte validateStatus: function (status) {

return status >= 200 && status < 300; / / the default

 },
maxRedirects Defines the maximum number of redirects for follow in Node.js Directs: 5, // By default
httpAgent httpAgenthttpsAgentUsed in Node.js to define custom proxies to use when performing HTTP and HTTPS, respectively.  httpAgent: new http.Agent({ keepAlive: true }),

 httpsAgent: new https.Agent({ keepAlive: true }),
proxy Define the host name and port of the proxy server proxy: {

Host: 127.0.0.1,

   port: 9000,

   auth: : {

     username: ‘mikeymike’,

     password: ‘rapunz3l’

   }

 },
cancelToken Specifies the Cancel token used to cancel the request cancelToken: new CancelToken(function (cancel) {

 })

1.2.1.2 Intercepting repeated requests

In the case of slow network speed, it is easy to appear that the user clicks and repeated requests make the page jitter, and the user experience is not good. Therefore, the processing of repeated requests is intercepted. Create a request queue —->

—– interception and processing —— Identify the request to be sent —-> Check whether the request to be sent is the same as the request in the queue —-> If they are the same, execute the cancellation method of the current request and delete the port from the request queue. > Create the cancellation method to be requested and put it into the queue for interception and processing

request.interceptors.request.use(
  config= > {
    // Intercepts repeated requests (i.e. the same request that is currently in progress)
    const requestData = getRequestIdentify(config, true); // Identify the request
    removePending(requestData, true);// Cancel duplicate requests
    config.cancelToken = new CancelToken((c) = > { // Create a cancellation method for the current request
      pending[requestData] = c;
    });
    return config;
  }, error= > {
  	return Promise.reject(error)
	})
Copy the code

Identify the request

const getRequestIdentify = (config, isReuest = false) = > {
  let url = config.url;
  if (isReuest) {
    url = config.baseURL + config.url.substring(1, config.url.length);
  }
  return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data));
};
Copy the code

Cancel duplicate requests

const pending = {};
const CancelToken = axios.CancelToken;
const removePending = (key, isRequest = false) = > {
  if (pending[key] && isRequest) {
    pending[key]('Cancel duplicate request');
  }
  delete pending[key];
};
Copy the code

1.2.2 Interface Response Interception

Interface response interception is mainly to extract and encapsulate the data returned by the interface, and uniformly configure the request exception.

request.interceptors.response.use(
  response= > {
    const data = response.data || {};
    return data;
  },
  error= > {
    const code = error.response.status;
    if (code) {
      let msg = ' ';
      switch (code) {
        case 400:
          msg = 'Request error';
          break;
        case 401:
          msg = 'Not authorized, please log in';
          break;
        case 403:
          msg = 'Access denied';
          break;
        case 404:
          msg = ` request${error.response.config.url}404 error ';
          break;
        case 408:
          msg = 'Request timed out';
          break;
        case 500:
          msg = 'Server internal error';
          break;
        case 501:
          msg = 'Service not implemented';
          break;
        case 502:
          msg = 'Gateway error';
          break;
        case 503:
          msg = 'Service unavailable';
          break;
        case 504:
          msg = 'Gateway timed out';
          break;
        case 505:
          msg = 'HTTP version not supported ';
          break;
      }
      Message.error(msg);
    }
    return Promise.reject(error); })Copy the code

1.2.3 API method encapsulation

The interface request method is encapsulated separately. The parameter of GET method is params, and the parameter of POST method is data.

// api.js
import request from '@/utils/request';

export function APIPostMethod(data) { // Custom interface methods
  return request({
    url: '/url1'.method: 'post',
    data
  });
}
export function APIGetMethod(params) { // Custom interface methods
  return request({
    url: '/url2'.method: 'get',
    params
  });
}
Copy the code

Call API methods in the business

import { APIGetMethod, APIPostMethod } from '@/utils/request';
const params = {}
APIGetMethod(params).then(res= > {
/ /...
// Data processing
})
Copy the code

1.3 Cross-domain processing

To put it simply, the same origin policy restrictions of the browser to prevent XSS and CSFR attacks create cross-domain problems when the front and back ends are developed separately. That is, if the request and response are not in the same protocol, domain name, and port, the browser will not allow the request. However, the same origin policy is a policy of the browser, not a part of the HTTP protocol. Therefore, the server invokes the HTTP interface only through THE HTTP protocol, not through the browser, and does not execute JS scripts. Therefore, the same origin policy mechanism is not triggered, and cross-domain problems do not exist. To solve this problem, you can configure the proxy server in the front end. Node.js middleware agent in VUe-CLI, Node +webpack+webpack-dev-server agent interface cross domain.

//vue.config.js
const config = {
  // ...
  devServer: {
    hot: true.open: true.host: '127.0.0.1'.// host: '0.0.0.0',// If this is a real machine test, use this IP
    port: 8899.https: false.hotOnly: false.proxy: {
      '/': {
        target: 'http://xxx.xxx.xx.xx:xxxx/'.logLevel: 'debug'.ws: false.changOrigin: true}}}}module.exports = config;
Copy the code

2.Nginx Reverse proxy Configure A proxy server through Nginx. The domain name is the same as local domain name A, but the port number is different.

# proxy server
server {
        listen       8088;
        server_name  _;
        client_max_body_size 50m;

        location / {
            root   html/dist;
            try_files $uri $uri/ /index.html;
            index  /index.html;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
        }
        location ^~/api {
            proxy_pass	http://xxx.xxx.xx.xx:xxxx/api;
            proxy_redirectdefault; }}Copy the code

When the front end starts the project, it needs to put the corresponding port 8088 of the project proxy on the front end (I feel this way is a bit redundant, but I dare not say that my colleagues use this way, so I will use the first one on my personal side). Nginx is more suitable for online deployment to solve cross-domain use, development environment, using VUE – CLI devServe is convenient and fast.


2. Vue skills

2.1 a lazy loading

Lazy loading, also known as lazy loading, causes components to load asynchronously. The goal is to delay the loading of non-essential resources and reduce the page loading time to optimize page performance.

2.1.1 Lazy Route loading

export default new Router({
  routes:[
    {
     path: '/test'.name: 'test'./ / lazy loading
     component: resolve= > require(['.. /page/test.vue'], resolve),
    },
  ]
})
Copy the code

In the case of route lazy loading, the code is divided into different code blocks according to the route, and the corresponding code block is loaded only when switching to the corresponding route, which makes the loading more efficient.

2.1.2 Component Lazy loading

components: {
  UpdateModal: resolve= > { require(['./UpdateNormalTaskModal'], resolve); }},Copy the code

Under the premise of route lazy loading, the comparative experiment of component lazy loading is carried out.

Lazy loading without using components:



The entire page is a JS with a size of 718KB and a loading time of 166ms.

When using component lazy loading:



The whole page is split into three JS (53.js), in which the lazy two components each have a JS (78.js and 91.js). It can be seen that the file size of 53.js is smaller and the loading speed is faster. Splitting one JS into multiple parallel loading can improve the loading efficiency and thus improve the performance.

2.2 On demand

Loading on demand is generally used when third-party libraries are used to avoid excessive pressure on the first screen loading caused by excessive third-party libraries. Take VantUI loading on demand as an example

  • Install Babel – plugin – import

    npm i babel-plugin-import -D
  • Configure plugins in babel.config.js
module.exports = {
  plugins: [['import', {
      libraryName: 'vant'.libraryDirectory: 'es'.style: true
    }, 'vant']],presets: [
    '@vue/app']};Copy the code
  • Introduce use in vUE files that use tripartite components
import { Swipe, SwipeItem, ImagePreview } from 'vant';
export default {
  components: {
    vanSwipe: Swipe,
    vanSwipeItem: SwipeItem,
  }
}
Copy the code

Use local image resources in 2.3 JS

When a local resource is used in vue’s V-bind syntax, the path is relative to the local folder and cannot be resolved when packing.

2.3.1. Static Resource reading

Put the image resources in the static static resources folder. When using SRC, access the resources in the root directory directly

For example, if the image is placed in the public directory, the path is directly written as'/img/case/gkjg/7.jpg'

2.3.2 Importing Resources

Import the image resource using require in data, then use the imgUrl variable.

data(){
  return {
		imgUrl:require(".. /assets/test.png")}}Copy the code

2.4 keep-alive

contains components that are cached and do not render DOM again to save performance. The activated and deactivated lifecycle hook functions are activated when the content is switched, and the cached components retain the status of the current component.

2.4.1 Routing Page Caching

Use the Meta field of the router

/ /... router.js
export default new Router({
  routes: [{path: '/'.name: 'Hello'.component: Hello,
      meta: {
        keepAlive: false // No caching is required}}, {path: '/page1'.name: 'Page1'.component: Page1,
      meta: {
        keepAlive: true // Need to be cached}}}])Copy the code
<keep-alive> <router-view v-if="$route.meta.keepAlive"></router-view> </keep-alive> <router-view v-if="! $route.meta.keepAlive"></router-view>Copy the code

2.4.2 Component Caching

<keep-alive include="test-keep-alive"> <! <component></component> </keep-alive> <keep-alive include="a,b"> <! <component :is="view"></component> </keep-alive> <! Using regular expressions, Need to use the v - bind - > < keep alive: - include = "/ a | b/" > < component: is =" view "> < / component > < / keep alive - > < keep alive exclude="test-keep-alive"> <! <component></component> </keep-alive>Copy the code

2.4.3 combining berforeRouteEnter

KeepAlive with the beforeRouteLeave(to, from, next) hook, set to.meta.keepAlive to specify whether the destination page is keepAlive.

export default {
    data() {
        return {};
    },
    methods: {},
    beforeRouteLeave(to, from, next) {
         // Set the meta of the next route
        to.meta.keepAlive = true;  // When the current page jumps to the next page, the destination page is cached and not refreshednext(); }};Copy the code

The proper use of keep-alive, combined with the activated and deactivated hook functions, caches the content that does not need to be updated and stores the content that needs to be updated in the two hooks. In this way, unnecessary HTTP requests and DOM repeated rendering are reduced and performance is improved

2.5 Customizing V-Model Commands

2.5.1 Ordinary V-Model

  • Application scenarios

Some states are modified in both the parent and child components. At the same time, the parent component will write some business logic when the state changes. If we use props and event listening methods, the code of the component will be very long and ugly, so I personally like to use V-Model to implement the bidirectional binding of the parent component. Very convenient!

  • V – the role of the model

V-model is often used for form components such as input to implement bidirectional binding of form values. This means that a value can be passed to the form from outside, and when you change the value of the form, the value of the form will also be changed, so their effect is bidirectional.

  • The essence of a V-Model is actually a syntactic sugar, as in the input element
<! <Input v-model="username"> <! <Input :value="username" @input="username=$event">Copy the code

2.5.2 Customizing v-Models

Since the V-model is a syntax-sugar, the props and $emit are needed, but it is up to us to declare which propd the V-model points to and which $emit event it corresponds to. Take a sample example:

// Calls to the Demo component in the parent component
<demo v-model="currentData" :other="otherProps"></demo>
Copy the code

Bind a value to the parent component

// demo.vue <template> <div> < button@click ="cEvent"> Click </button> {{current}} </div> </template> <script> export default { props: { current: { type: String }, other: { type: String } }, watch:{ current(newVal,oldVal) { ... doSomething... }}, model: {prop: 'current', event: 'event'}, methods: {cEvent() {this.$emit('event', 'change '); }}}; </script>Copy the code

The default behavior for changing model is declared in the child component, which specifies that the pass to the V-model is current in the props. When the value of current is changed, $emit an event to notify the parent component to change the value of current, which implements the bidirectional binding of the V-Model. The above behavior is equivalent to writing the following using regular props and $emit

<template> <div> <demo :current"currentData" :other="otherProps" @event="changeCurrent"></demo> </div> </template> <script> export default {data() {return {currentData: 'initial'}}, methods: { changeCurrent(val) { this.changeCurrent = val ... doSomething... }}}; </script>Copy the code
// demo.vue <template> <div> < button@click ="cEvent"> Click </button> {{current}} </div> </template> <script> export default { props: { current: { type: String }, other: { type: String } }, watch:{ current(newVal,oldVal) { ... doSomething... {}} the methods: {cEvent () enclosing $emit (' event ', 'changed'); }}}; </script>Copy the code

2.6 Dynamic Components + Automatic registration of Components

Scene:

  • You need to load a folder’s VUE files dynamically
  • There are too many files to manually import

2.6.1 Automatic Registration

// This code is written directly in the script tag
var templatePage = {};
var nameList = [];
function initPage() {
  /* require. Context () : /* require. Context () : /* require
  const requireComponent = require.context(
    // The relative path of its component directory
    '.. /views/template'.// Whether to query the subdirectory
    true.// A regular expression that matches the underlying component file name
    /.vue$/
  );
  requireComponent.keys().forEach(fileName= > {
    var names = fileName
      .split('/') [1] + The '-' + fileName
      .split('/') [2].split('. ') [0];
    const componentConfig = requireComponent(fileName);
    templatePage[names] = componentConfig.default || componentConfig;
    nameList.push(names);
  });
}
initPage();
Copy the code
components: { ... templatePage },Copy the code

This method can also be used to automatically import image resources

2.6.2 Dynamic Components

Components are matched by IS, so they must have names.

// Template <component :is="comp" v-for="comp in fileList" :id="comp" :key="comp" class="content__item"> </component>Copy the code
computed: {
    fileList() {
      return nameList
        .filter(v= > v.split(The '-') [0= = =this.$route.params.templateId); }},Copy the code


2.7 Encapsulating custom instructions

Push-button level granular permission control of official documents is implemented using the custom instruction V-Permission. The directive is filtered by the permissions array passed in and the current user role array to see if the user has the required permissions, otherwise remove the dom element that the directive hooks into

// permission.js

export default {
  // el - Mount dom
  // binding- v-per="[]" {value: []}
	inserted(el,binding) {
    // Get the value passed in
  	const {value: permissionRoles} = binding;
    / /... The business logic
    // Get the user role
    const roles = stroe.getters.roles;
    // Check the validity
    if(permissionRoles && permissionRoles instanceof Array
			permissionRoles.length > 0) {
      const hasPermission = roles.some(role= > {
        return permissionRoles.includes(role)
      });
      if(!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el);
      }
    } else {
    	throw new Error('Need to specify array type permissions'); }}}Copy the code
//main.js
import permission from '@.. /permission; // Register directive vue. directive('permission',permission);
Copy the code

2.8 a decorator

The Decorator syntax has not yet been proposed, so the project rarely uses blog.csdn.net/weixin_3423… Juejin. Cn/post / 685651…

2.9 Uploading a File Progress Bar

Use Axios’ onUploadProgress and onDownloadProgress to get the upload and download progress, respectively, in the same way you configure config such as contentType.

onUploadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
}
    // `onDownloadProgress` allows handling of progress events for downloads
    // browser only
onDownloadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
}
Copy the code

3. The Element – the UI on pit

The UI library of the company’s background management system is Element. I have stepped on a lot of holes during this period, but I was busy with business and neglected to tidy up, so I don’t remember. Later, this piece will be gradually accumulated and recorded.

3.1 the Dialog

3.1.1 Lifecycle (Dialog as a child component)

The life cycle order of the child components is: Parent beforeCreate-> Parent created-> Parent beforeMount-> child beforeCreate-> child created-> child beforeMount-> child Mounted -> Parent Mounted

Therefore, el-Dialog enters a created/Mounted cycle after the parent component is created, regardless of whether the dialog is opened or not.

There are a lot of twists and turns in order to be able to perform the method of retrieving data when opened.

1. V-if control rendering method

In the parent component, v-if control is applied to the El-Dialog, and each display is re-rendered. You can then execute the method you want in the Created lifecycle.



2. Watch to monitor method

In the El-Dialog component, the listener controller displays hidden variables that, when true, represent the dialog display and execute the method you execute.



3. Open event callback method

Don’t mention that el-Dialog has an open callback that you didn’t notice before! Use the oEPN callback to execute your method in the callback and you’re done!

3.1.2 can drag and drop

Modify the Element-UI dialog component to be draggable so that the user can drag and drop to determine the location of the Dialog based on actual needs. The actual implementation refers to the code of big netizens, and according to the actual needs, the boundary judgment is cancelled. The concrete implementation is as follows:

  • Create the cachet.js file
import Vue from 'vue';

// v-dialogDrag: popup drag
Vue.directive('dialogDrag', {
  bind(el, binding, vnode, oldVnode) {
    // Get the drag header
    const dialogHeaderEl = el.querySelector('.el-dialog__header');
    The whole RRC-Dialog is a component that I wrapped myself. If a component uses Element, it should be written as.el-dialog
    const dragDom = el.querySelector('.el-dialog');
    dialogHeaderEl.style.cursor = 'move';

    CurrentStyle Firefox Google Window.getComputedStyle (DOM element, null);
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);

    // Mouse down event
    dialogHeaderEl.onmousedown = (e) = > {
      e.stopPropagation();
      // Mouse down to calculate the current element from the viewable area (mouse click position from the viewable window distance)
      const disX = e.clientX - dialogHeaderEl.offsetLeft;
      const disY = e.clientY - dialogHeaderEl.offsetTop;

      // The obtained value is replaced with a PX regular match
      let styL, styT;

      // Notice that in IE, the first value obtained is the component 50% and then assigned to px
      if (sty.left.includes(The '%')) {
        styL = +document.body.clientWidth * (+sty.left.replace(/\%/g.' ') / 100);
        styT = +document.body.clientHeight * (+sty.top.replace(/\%/g.' ') / 100);
      } else {
        styL = +sty.left.replace(/\px/g.' ');
        styT = +sty.top.replace(/\px/g.' ');
      }

      // Mouse drag events
      document.onmousemove = function(e) {
        e.stopPropagation();
        // With the event delegate, calculate the distance moved (start drag distance to end drag distance)
        const l = e.clientX - disX;
        const t = e.clientY - disY;

        let finallyL = l + styL;
        let finallyT = t + styT;

        // Note that the difference between clientWidth and scrollWidth is subtracted from the previous top left value
        // dragdom.offsetParent represents the shaded part of the popover
        if (finallyL < 0) {
          // finallyL = 0;
        } else if (finallyL > dragDom.offsetParent.clientWidth - dragDom.clientWidth - dragDom.offsetParent.offsetLeft) {
          finallyL = dragDom.offsetParent.clientWidth - dragDom.clientWidth - dragDom.offsetParent.offsetLeft;
        }

        if (finallyT < 0) {
          // finallyT = 0;
        } else if (finallyT > dragDom.offsetParent.clientHeight - dragDom.clientHeight - dragDom.offsetParent.offsetTop) {
          finallyT = dragDom.offsetParent.clientHeight - dragDom.clientHeight - dragDom.offsetParent.offsetTop;
        }

        // Move the current element
        dragDom.style.left = `${finallyL}px`;
        dragDom.style.top = `${finallyT}px`;

        // Pass out the location
        // binding.value({x:e.pageX,y:e.pageY})
      };

      document.onmouseup = function(e) {
        e.stopPropagation();
        document.onmousemove = null;
        document.onmouseup = null; }; }; }});Copy the code
  • Introduced in main.js
import './utils/directives';
Copy the code
  • You can do this by using the V-DialogDrag directive in the dialog TAB that you want to drag
<el-dialog v-dialogDrag custom-class="enter-dialog" title="""></el-dialog>
Copy the code

3.2 Table Reserve-selection cross-page selection

Reserve-selection property in el-table can be used to retain the selected data on the previous page after paging switch (cross-page selection).

Note: 1. Set row-key. The row-key must be a unique identifier for each data item. 2. When it comes to the switch of the table content and you do not want to save the check box, but do not re-render, remember to clear the check box to avoid misoperation.

3.3 Custom Styles

3.3.1 Style penetration

When element’s components are used in vue, they are usually used in component files, and each component is scoped to prevent naming style contamination. In this case, there are three ways to customize elment’s components:

<style scoped>
/* Outer >>> through */
.my-dialog >>> .el-dialog {
 background: #ff0;
}
/* /deep/ through */
/deep/ .el-dialog{
 background: #ff0;
}
</style>

/* A separate style that does not use scoped is used to set third-party style changes */
/* Global CSS with outer style restrictions */
<style>
.my-dialog .el-dialog{
  background: #ff0;
}
</style>
Copy the code

From debugging, we can see that we can only modify the elment component style inside the component, without affecting the global Element component style:

  • Under scoped, the outer layer >>> penetrates and adds the [data-v-***] unique identifier to the outer layer
  • /deep/ penetrates the identifier directly before the EL component
  • The method of setting a single global label and enclosing a style for local restrictions is nothing fancy, but it does the trick.
  • Priority of three: >>> Penetration > Global match outer style > /deep/








Note:

  • /deep/ is Chrome’s own standard and may not work on other browsers
  • <<< penetration cannot be parsed on some preprocessors





3.3.2 Special drop-down box style

Pit:

When customizing component styles that include drop-down boxes, such as the el-Select drop-down, the part of the drop-down box is detached from the component file and leveled directly with the outermost app.vue, which means that no outer styles can cover the drop-down box. So there is no penetration, no penetration can come out.



Official documents:

When making style changes under Propper-class, only style without scoped can be used. The name of the propper-class class must be unique to ensure that the dropdown boxes on each page are not affected.

Components that have the propper-class attribute are:

3.4 the Image

3.4.1 Default white background when loading

When loading, the image will have a default white background, loading moment is difficult to capture which CSS in the demon, so have to go to the source code.

// node_modules/element-ui/packages/image/src/main.vue



You see the el-Image__placeholder class doing the monster. Then you can rewrite the style in your own component and change it to the color you want.