笔记(摘自blog)

块级格式化上下文BFC

BFC是block formatting context,在文档流的类型中,普通文档流属于FC,而BFC可以理解为一种占位的普通文档流。它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及和其他元素之间的关系和作用。

-----------------------------------------------------------------

传统的写法

首先我们回顾下require.js的写法。假设我们有两个js文件: index.jscontent.js,现在我们想要在index.js中使用content.js返回的结果,我们要怎么做呢?

//首先定义:

//content.js
define('content.js', function(){
    return 'A cat';
})
//然后require:

//index.js
require(['./content.js'], function(animal){
    console.log(animal);   //A cat
})

那CommonJS是怎么写的呢?

//index.js
var animal = require('./content.js')

//content.js
module.exports = 'A cat'

ES6的写法

//index.js
import animal from './content'

//content.js
export default 'A cat'


---------------------------------------------------------------------------------------------------------------------------------------------------------------
//content.js

export default 'A cat'    
export function say(){
    return 'Hello!'
}    
export const type = 'dog' 

上面可以看出,export命令除了输出变量,还可以输出函数,甚至是类(react的模块基本都是输出类)

//index.js

import { say, type } from './content'  
let says = say()
console.log(`The ${type} says ${says}`)  //The dog says Hello

这里输入的时候要注意:大括号里面的变量名,必须与被导入模块(content.js)对外接口的名称相同。

如果还希望输入content.js中输出的默认值(default), 可以写在大括号外面。

//index.js

import animal, { say, type } from './content'  
let says = say()
console.log(`The ${type} says ${says} to ${animal}`)  
//The dog says Hello to A cat

修改变量名

此时我们不喜欢type这个变量名,因为它有可能重名,所以我们需要修改一下它的变量名。在es6中可以用as实现一键换名。

//index.js

import animal, { say, type as animalType } from './content'  
let says = say()
console.log(`The ${animalType} says ${says} to ${animal}`)  
//The dog says Hello to A cat


--------------------------------------------------------------------------------------------------------------------------
function view() {
  return <ul id="filmList" className="list">
    <li className="main">Detective Chinatown Vol 2</li>
    <li>Ferdinand</li>
    <li>Paddington 2</li>
  </ul>
}

// 接下来,我们要将JSX编译成js, 也就是hyperscript。我们先用Babel编译一下,看这段JSX转成js会是什么样子,打开命令行,输入npm run compile,得到的compile.js:

function view() {
  return h(
    "ul",
    { id: "filmList", className: "list" },
    h(
      "li",
      { className: "main" },
      "Detective Chinatown Vol 2"
    ),
    h(
      "li",
      null,
      "Ferdinand"
    ),
    h(
      "li",
      null,
      "Paddington 2"
    )
  );
}

可以看出h函数接收的参数,第一个参数是node的类型,比如ul,li,第二个参数是node的属性,之后的参数是node的children,假如child又是一个node的话,就会继续调用h函数。

清楚了Babel会将我们的JSX编译成什么样子后,接下来我们就可以继续在index.js中来写h函数了。

function flatten(arr) {
  return [].concat(...arr)
}

function h(type, props, ...children) {
  return {
    type,
    props: props || {},
    children: flatten(children)
  }
}

我们的h函数主要的工作就是返回我们真正需要的hyperscript对象,只有三个参数,第一个参数是节点类型,第二个参数是属性对象,第三个是子节点的数组。

flatten(children)这个操作是因为children这个数组里的元素有可能也是个数组,那样就成了一个二维数组,所以我们需要将数组拍平成一维数组。[].concat(...arr)是ES6写法,传统的写法是[].concat.apply([], arr)

我们现在可以先来看一下h函数最终返回的对象长什么样子。

 
function render() {
  console.log(view())
}
// 下面我们就可以根据VDOM, 来渲染真实DOM了。先改写render函数:

function render(el) {
  el.appendChild(createElement(view(0)))
}
function createElement(node) {
  if (typeof(node) === 'string') {
    return document.createTextNode(node)
  }

  let { type, props, children } = node
  const el = document.createElement(type)
  setProps(el, props)
  children.map(createElement)
    .forEach(el.appendChild.bind(el))

  return el
}

function setProp(target, name, value) {
  if (name === 'className') {
    return target.setAttribute('class', value)
  }

  target.setAttribute(name, value)
}

function setProps(target, props) {
  Object.keys(props).forEach(key => {
    setProp(target, key, props[key])
  })
}


原文地址:https://www.cnblogs.com/fengnovo/p/9013296.html