motivation
Feel now business development, if the demand is not very special, basic can find components used in the corresponding components of the rolls, such code is calling component, but the thought of hidden within the components, thus weakening the ability of programming, so I want to write such a series to motivate yourself deeply analysis the principle of component, improve code reading comprehension, I think it’s important to write something down, because if you just read it and don’t write it you forget it, so you’re going to keep writing this analysis, right
Element source code
The official website portal is here, and the main catalog is shown below
package
src
package
theme-chalk
Analysis of source code
<el-row>
Source code analysis
First of all, enter the official website to view the explanation of the relevant part of Layout, and find that there are two main components: El-row and el-col represent rows and columns respectively, similar to col and row in bootstrap. First, let’s look at the implementation of el-Row. Enter the row folder in package, which contains a SRC folder and index.js file
index.js
Row
import
install
Vue.use()
render
render
export default
import Row from './src/row';
/* istanbul ignore next */
Row.install = function(Vue) {
// Register this component globally (common components are best registered globally)
Vue.component(Row.name, Row);
};
export default Row;
Copy the code
There are actually two ways to use a component. One is to use it as a plug-in, but directly import it and register it. The sample code on the official website is as follows
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
Use (Button) * vue.use (Select) */
new Vue({
el: '#app'.render: h= > h(App)
});
Copy the code
SRC /row.js: SRC /row.js: SRC /row.js: SRC /row.js: SRC /row.js: SRC /row.js
export default{... }Copy the code
The amount of code for the entire component is small, and detailed comments are provided below
exportDefault {// The name of the component, note the camel name, which makes it possible to use the name of both the short line connection <el-row> and the camel name <ElRow> when using the component:'ElRow'// Custom attribute (this attribute is not a component required attribute), important for <el-col>'ElRow', // The component's props props: {// The actual tag that the component renders to HTML is div tag: {type: String,
default: 'div'}, // inside the <el-col> component gutter: Number, /* whether the component is a Flex layout, willtypeProperty is assigned to'flex'Flex layout can be enabled, * and values in start, Center, end, space-between, and space-around * can be specified using the justify attribute to define the layout of child elements. * /type: String, // Flex layout attribute justify: {type: String,
default: 'start'}, // Flex layout align attribute align: {type: String,
default: 'top'}}, computed: {// Left and right margin of row, used to offset the padding of colstyle() {
const ret = {};
if (this.gutter) {
ret.marginLeft = `-${this.gutter / 2}px`;
ret.marginRight = ret.marginLeft;
}
returnret; }}, render(h) {// render(h)return h(this.tag, {
class: [
'el-row', this.justify ! = ='start' ? `is-justify-${this.justify}` : ' ', this.align ! = ='top' ? `is-align-${this.align}` : ' ',
{ 'el-row--flex': this.type === 'flex' }
],
style: this.style
}, this.$slots.default); }};Copy the code
The gutter attribute calculates the left and right margins of this component, which is a negative number. Here’s how the gutter attribute translates to the col in the row creating a space. Note that the left and right sides of the container have no space
<el-row>
<el-col>
<el-row>
box-sizing
border-box
gutter
<el-row>
$parent
<el-col>
padding-left
padding-right
<el-row>
-gutter/2+'px'
Note that if you look at the image above, it is common to use margin between col, but it is not easy to use padding, width is divided by percentage (box-sizing should be set to border-box).
The first argument is the tag name of the HTML (the name of the tag that will eventually be displayed on the page), and the second argument is a data object containing template-related attributes, which contains a number of template-related attributes, as follows
{// The same API as' v-bind:class '// accepts a string, object, or array of strings and objects'class': {
foo: true,
bar: false}, // The same API as' v-bind:style '// accept a string, object, or array of objects style: {color:'red',
fontSize: '14px'}, // Normal HTML attrs: {id:'foo'
},
// 组件 props
props: {
myProp: 'bar'}, // DOM properties domProps: {innerHTML:'baz'}, // Event listeners are based on 'on' // so modifiers such as' V-on :keyup. Enter 'are no longer supported // Need to manually match keyCode. On: {click: this.clickHandler}, // for components only, used to listen for native events, not for component internal use // 'VM.$emit'triggered event. NativeOn: {click: this.nativecLickHandler}, // custom directive. Note that you cannot assign a value to 'oldValue' // in 'binding' because Vue has automatically synchronized for you. directives: [ { name:'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true}}], / / / / scope slot format {name: props = > VNode | Array < VNode >} scopedSlots: {default: props = > createElement method ('span', props. Text)}, // If the component is a child of another component, give the slot a name:'name-of-slot'// Other special top-level attributes key:'myKey',
ref: 'myRef'
}
Copy the code
In the case of String, it represents the contents of the text node, which is a text node. In the case of Array, it is a child node, and each value in the Array is a render argument function
[// Text node'Write some words first.',
createElement('h1'.'A headline'),
createElement(MyComponent, {
props: {
someProp: 'foobar'}})]Copy the code
The default attribute contains all nodes that are not included in the named slots. The default attribute contains all nodes that are not included in the named slots. For code like this, the render function renders
and
test
as its children
<el-row>
<h1>test<h1>
<slot name='t'>t1</slot>
</el-row>
Copy the code
SCSS (packages/theme-chalk/ SRC /row. SCSS); SCSS (SCSS)
class:[
'el-row', this.justify ! = ='start' ? `is-justify-${this.justify}` : ' ', this.align ! = ='top' ? `is-align-${this.align}` : ' ',
{ 'el-row--flex': this.type === 'flex'}].Copy the code
defaults to the full width of the parent container and the height auto ADAPTS
<el-col>
Source code analysis
Col is also simple to use, as followsspan
.offset
.pull
.push
Attributes such as
<el-col :span="6" :offset="6"><div class="grid-content bg-purple"></div></el-col>
Copy the code
Enter package/ COL to check, col code is a little longer, the main extra logic is control adaptive (@media screen)
exportDefault {// Component name:'ElCol', props: {// Number of props to the parent container, total of 24 columns. If set to 0, render.type: Number, default: 24}, // Final render tag name, default div tag: {type: String,
default: 'div'}, // Specify the Number of columns to the right offset: Number, // grid to the right pull: Number, // grid to the left push: Number, // responsive correlation xs: [Number, Object], sm: [Number, Object], md: [Number, Object], lg: [Number, Object], xl: [Number, Object] }, computed: {// Get the gutter value of el-rowgutter() {
let parent = this.$parent; // componentName is a custom attribute set to an El-Row component to determine if it is an El-Row componentwhile (parent && parent.$options.componentName ! = ='ElRow') {
parent = parent.$parent;
}
return parent ? parent.gutter : 0;
}
},
render(h) {
let classList = [];
letstyle = {}; Gutter = 2 padding (left and rightif (this.gutter) {
style.paddingLeft = this.gutter / 2 + 'px'; style.paddingRight = style.paddingLeft; } // Handle layout dependencies, more on that later ['span'.'offset'.'pull'.'push'].forEach(prop => {
if(this[prop] || this[prop] === 0) { classList.push( prop ! = ='span'
? `el-col-${prop}-${this[prop]}`
: `el-col-${this[prop]}`); }}); // Handle screen responsive correlation ['xs'.'sm'.'md'.'lg'.'xl'].forEach(size => {
if (typeof this[size] === 'number') {
classList.push(`el-col-${size}-${this[size]}`);
} else if (typeof this[size] === 'object') {
letprops = this[size]; Object.keys(props).forEach(prop => { classList.push( prop ! = ='span'
? `el-col-${size}-${prop}-${props[prop]}`
: `el-col-${size}-${props[prop]}`); }); }});return h(this.tag, {
class: ['el-col', classList],
style
}, this.$slots.default); }};Copy the code
[‘span’, ‘offset’, ‘pull’, ‘push’], span is the number of columns in the parent container, corresponding to the SCSS code as follows
[class*="el-col-"] {
float: left;
box-sizing: border-box;
}
.el-col-0 {
display: none;
}
@for $i from 0 through 24 {
.el-col-#{$i} {
width: (1 / 24 * $i * 100) * 1%;
}
.el-col-offset-#{$i} {
margin-left: (1 / 24 * $i * 100) * 1%;
}
.el-col-pull-#{$i} {
position: relative;
right: (1 / 24 * $i * 100) * 1%;
}
.el-col-push-#{$i} {
position: relative;
left: (1 / 24 * $i* * 100) 1%; }}Copy the code
Note the [attribute*=value] selector, which selects all classes whose names start with el-col-. Add float and border-box. Horizontal float is a must. The width of the el-col-numeric class is the percentage. The offset below is actually margin-left, which may result in a line that doesn’t fit all the cols in one line, resulting in line breaks. El-col-pull is different, it just moves relative to the original position. Instead of causing a line break, it will cause different cols to overlay each other
Note that the JS section above makes heavy use of template strings rather than string concatenation to simplify the code, which is worth learning