├── README.zh.md
├── README.ja.md
├── README.md
└── minilogo.svg
/README.zh.md:
--------------------------------------------------------------------------------
1 | # vuera
2 |
3 | [](https://github.com/chinanf-boy/Source-Explain)
4 |
5 | 解释
6 |
7 | >"version": "0.1.3"
8 |
9 | [github source](https://github.com/topics/vuera)
10 |
11 | ---
12 |
13 | 同时在 react 与 vue 中 运行。
14 |
15 | - [react 在 vue](#ReactInVue)
16 |
17 | - [vue 在 react](#VueInReact)
18 |
19 | ---
20 |
21 | - 其他
22 |
23 | - [isReactComponent 函数 实现](#isreactcomponent)
24 |
25 | ---
26 |
27 | 两种情况
28 |
29 | 先说第一种:
30 |
31 | # ReactInVue
32 |
33 | ``` js
34 | import Vue from 'vue'
35 | import { VuePlugin } from 'vuera'
36 |
37 | Vue.use(VuePlugin)
38 | /* ... */
39 | ```
40 |
41 | Now, use your React components like you would normally use your Vue components!
42 |
43 | ``` vue
44 |
45 |
46 |
I'm a Vue component
47 |
48 |
49 |
50 |
51 |
66 | ```
67 |
68 | 以 Vue插件的形式 用 React
69 |
70 | vuera/src/VuePlugin.js
71 | ``` js
72 | // 判断是否 React的组件
73 | import isReactComponent from './utils/isReactComponent'
74 | // React Component -> Vue Component
75 | import VueResolver from './resolvers/Vue'
76 |
77 | /**
78 | * vue 插件
79 | */
80 | export default {
81 | install (Vue, options) {
82 | /**
83 | * 自定义合并策略,这个策略真的只是
84 | * 包装所有React组件,同时保留Vue组件。
85 | */
86 | const originalComponentsMergeStrategy = Vue.config.optionMergeStrategies.components
87 |
88 | Vue.config.optionMergeStrategies.components = function (parent, ...args) {
89 | // 之前设置的 值 <-- return Object.assign(parent, wrappedComponents)
90 | const mergedValue = originalComponentsMergeStrategy(parent, ...args)
91 |
92 | //
93 | const wrappedComponents = mergedValue
94 | ? Object.entries(mergedValue).reduce(
95 | (acc, [k, v]) => ({
96 | ...acc,
97 | [k]: isReactComponent(v) ? VueResolver(v) : v,
98 | }),
99 | {}
100 | )
101 | : mergedValue
102 | //合并
103 | return Object.assign(parent, wrappedComponents)
104 | }
105 | },
106 | }
107 |
108 | ```
109 |
110 | 其中出现
111 |
112 | - [install()](https://cn.vuejs.org/v2/guide/plugins.html#%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6)
113 |
114 | - [vue.config 是一个对象,包含 Vue 的全局配置。可以在启动应用之前修改下列属性:](https://cn.vuejs.org/v2/api/#optionMergeStrategies)
115 |
116 | - [Object.entries](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)
117 |
118 | ``` js
119 | const obj = { foo: 'bar', baz: 42 };
120 | console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
121 | ```
122 | - [Array.prototype.reduce](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
123 |
124 | ``` js
125 | var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
126 | return a.concat(b);
127 | }, []);
128 | // flattened is [0, 1, 2, 3, 4, 5]
129 | ```
130 |
131 | - VueResolver
132 |
133 | vuera/src/resolvers/Vue.js
134 | > React Component -> Vue Component
135 |
136 | 那么可以看出,使用遍历了一边 ``Vue``组件,如果发现 ``React``组件的样子 就改造成``Vue``。
137 |
138 | 再说第二种:
139 |
140 | # VueInReact
141 |
142 | 加 `vuera/babel` 到 你的`.babelrc` `plugins` 选项
143 |
144 | .babelrc
145 | ``` json
146 | {
147 | "presets": "react",
148 | "plugins": ["vuera/babel"]
149 | }
150 | ```
151 |
152 | 使用
153 |
154 | ``` jsx
155 | import React from 'react'
156 | import MyVueComponent from './MyVueComponent.vue'
157 |
158 | export default () => (
159 |
160 |
I'm a react component
161 |
162 |
163 |
164 |
165 | )
166 | ```
167 |
168 | 从 添加 ``.babelrc`` 的方式来看,难道是用 babel
169 |
170 | vuera/babel.js
171 | ``` js
172 | /* eslint-env node */
173 |
174 | function processCreateElement (maybeReactComponent, args, file, path, types) {
175 | // If the first argument is a string (built-in React component), return
176 | if (maybeReactComponent.type === 'StringLiteral') return
177 |
178 | if (!file.insertedVueraImport) {
179 | file.path.node.body.unshift(
180 | types.importDeclaration(
181 | [
182 | types.importSpecifier(
183 | types.identifier('__vueraReactResolver'),
184 | types.identifier('__vueraReactResolver')
185 | ),
186 | ],
187 | types.stringLiteral('vuera')
188 | )
189 | )
190 | }
191 | // Prevent duplicate imports
192 | file.insertedVueraImport = true
193 |
194 | // Replace React.createElement(component, props) with our helper function
195 | path.replaceWith(
196 | types.callExpression(types.identifier('__vueraReactResolver'), [maybeReactComponent, ...args])
197 | )
198 | }
199 |
200 | // types 是 babel 插件 API 其中一个模块
201 | module.exports = function ({ types }) {
202 | return {
203 | visitor: {
204 | CallExpression (path, { file }) {
205 | const callee = path.node.callee
206 | const [maybeReactComponent, ...args] = path.node.arguments
207 |
208 | // 如果有 react 模块 ,reactImport
209 | const reactImport = file.path.node.body.find(
210 | x => x.type === 'ImportDeclaration' && x.source.value === 'react'
211 | )
212 | if (!reactImport) return
213 |
214 | // 如果 CallExpression 是 react.createElement
215 | if (callee.type === 'MemberExpression') {
216 | /**
217 | * 获取默认导入名称. Examples:
218 | * import React from 'react' => "React"
219 | * import hahaLOL from 'react' => "hahaLOL"
220 | */
221 | const defaultImport = reactImport.specifiers.find(
222 | x => x.type === 'ImportDefaultSpecifier'
223 | )
224 | if (!defaultImport) return
225 | const reactName = defaultImport.local.name
226 |
227 | const { object, property } = callee
228 | if (!(object.name === reactName && property.name === 'createElement')) {
229 | return
230 | }
231 |
232 | processCreateElement(maybeReactComponent, args, file, path, types)
233 | }
234 | // 检查 CallExpression 是 react's 'createElement'
235 | if (callee.type === 'Identifier' && callee.name !== '__vueraReactResolver') {
236 | // Return unless createElement was imported
237 | const createElementImport = reactImport.specifiers.find(
238 | x => x.type === 'ImportSpecifier' && x.imported.name === 'createElement'
239 | )
240 | if (!createElementImport) return
241 |
242 | processCreateElement(maybeReactComponent, args, file, path, types)
243 | }
244 | },
245 | },
246 | }
247 | }
248 |
249 | ```
250 |
251 | - [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types)
252 |
253 | - [babel-AST](http://web.jobbole.com/88236/)
254 |
255 | - [babel-语法插件中文](https://github.com/thejameskyle/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-api)
256 |
257 | - [在线AST语法树](http://astexplorer.net/)
258 |
259 | 这部分需要理解 AST语法树的 问题 , 上面的链接能帮助你简单明确 ``babel`` 的 ``plugins`` 插件中 ``ImportSpecifier`` ``MemberExpression`` 之类 问题
260 |
261 | >babel.js中总得来说,就是把 ``react.createElement`` 变成 ``__vueraReactResolver`` 内置函数
262 |
263 | ```js
264 | export function babelReactResolver (component, props, children) {
265 | return isReactComponent(component)
266 | ? React.createElement(component, props, children)
267 | : React.createElement(VueWrapper, Object.assign({ component }, props), children)
268 | }
269 |
270 | // babelReactResolver as __vueraReactResolver
271 | ```
272 |
273 | 上面的只是再 ``babel`` 将 ``js`` 降级时所作的事情
274 |
275 | ---
276 |
277 | VueWrapper 上面代码中 👄最重要的
278 |
279 | vuera/src/wrapper/vue.js
280 | ``` js
281 | import React from 'react'
282 | import Vue from 'vue'
283 | import ReactWrapper from './React'
284 |
285 | const VUE_COMPONENT_NAME = 'vuera-internal-component-name'
286 |
287 | const wrapReactChildren = (createElement, children) =>
288 | createElement('vuera-internal-react-wrapper', {
289 | props: {
290 | component: () => {children}
,
291 | },
292 | })
293 |
294 | export default class VueContainer extends React.Component {
295 | constructor (props) {
296 | super(props)
297 |
298 | /**
299 | * 传入并重新定义真正的 组件
300 | * `component` prop.
301 | */
302 | this.currentVueComponent = props.component
303 |
304 | /**
305 | * 修改createVueInstance函数以正确传递此绑定。 在做这个
306 | * 构造函数避免在渲染中实例化函数。
307 | * //我觉得有点难理解 :译者曰
308 | */
309 | const createVueInstance = this.createVueInstance
310 | const self = this
311 | this.createVueInstance = function (element, component, prevComponent) {
312 | createVueInstance(element, self, component, prevComponent)
313 | }
314 | }
315 |
316 | componentWillReceiveProps (nextProps) {
317 | const { component, ...props } = nextProps
318 |
319 | if (this.currentVueComponent !== component) {
320 | this.updateVueComponent(this.props.component, component)
321 | }
322 | /**
323 | * NOTE: 没有去比较 props 和 nextprops, because I didn't want to write a
324 | * function for deep object comparison. I don't know if this hurts performance a lot, maybe
325 | * we do need to compare those objects.
326 | */
327 | Object.assign(this.vueInstance.$data, props)
328 | }
329 |
330 | componentWillUnmount () {
331 | this.vueInstance.$destroy()
332 | }
333 |
334 | /**
335 | * 创建和加载 VueInstance 接口
336 | * NOTE: VueInstance inside VueContainer
337 | * 我们不能绑定 createVueInstance 到 这个 VueContainer 对象, 需要明确
338 | * 传递 绑定对象
339 | * @param {HTMLElement} targetElement - element to attact the Vue instance to
340 | * @param {ReactInstance} reactThisBinding - current instance of VueContainer
341 | */
342 | createVueInstance (targetElement, reactThisBinding) {
343 | const { component, ...props } = reactThisBinding.props
344 |
345 | // `this` refers to Vue instance in the constructor
346 | reactThisBinding.vueInstance = new Vue({
347 | // targetElement 就是 VueContainer render() element
348 | el: targetElement,
349 | data: props,
350 | render (createElement) {
351 | return createElement(
352 | VUE_COMPONENT_NAME,
353 | {
354 | props: this.$data,
355 | },
356 | [wrapReactChildren(createElement, this.children)]
357 | )
358 | },
359 | components: {
360 | [VUE_COMPONENT_NAME]: component,
361 | 'vuera-internal-react-wrapper': ReactWrapper,
362 | },
363 | })
364 | }
365 |
366 | updateVueComponent (prevComponent, nextComponent) {
367 | this.currentVueComponent = nextComponent
368 |
369 | /**
370 | * Replace the component in the Vue instance and update it.
371 | */
372 | this.vueInstance.$options.components[VUE_COMPONENT_NAME] = nextComponent
373 | this.vueInstance.$forceUpdate()
374 | }
375 |
376 | render () {
377 | return
378 | }
379 | }
380 | ```
381 |
382 | 用 ``React.Component`` 包裹传入的 ``props.component`` 组件,然后内部新建 ``Vue实例 reactThisBinding.vueInstance``,
383 |
384 | - 相当于说 这部分 ```` 交给 Vue 处理, 然后把 Vue的部分事件 给予 react.component 组件事件处理调用。
385 |
386 | ---
387 |
388 | ## 其他
389 |
390 | ### isReactComponent
391 |
392 | ``` js
393 | export default function isReactComponent (component) {
394 | if (typeof component === 'object') {
395 | return false // no object == no react
396 | } else if (
397 | typeof component === 'function' &&
398 | component.prototype.constructor.super &&
399 | component.prototype.constructor.super.name.startsWith('Vue')
400 | ) {
401 | return false // is vue
402 | } else {
403 | return true // is react
404 | }
405 | }
406 |
407 | ```
--------------------------------------------------------------------------------
/README.ja.md:
--------------------------------------------------------------------------------
1 | # ヴェラ
2 |
3 | [](https://github.com/chinanf-boy/Source-Explain)
4 |
5 | 説明
6 |
7 | > "version": "0.1.3"
8 |
9 | [ギブスソース](https://github.com/topics/vuera)
10 |
11 | [中文版](./README.zh.md)
12 |
13 | * * *
14 |
15 | 反応して同時に動いている.
16 |
17 | - [vueに反応する](#ReactInVue)
18 |
19 | - [反応時の意見](#VueInReact)
20 |
21 | * * *
22 |
23 | - 続きを見る
24 |
25 | - [isreactcomponent](#isreactcomponent)
26 |
27 | * * *
28 |
29 | 2つの状況
30 |
31 | 最初に言った:
32 |
33 | # 反応する
34 |
35 | ```js
36 | import Vue from 'vue'
37 | import {VuePlugin} from 'vuera'
38 |
39 | Vue.use (VuePlugin)
40 | /* ... */
41 | ```
42 |
43 | 通常はあなたのvueコンポーネントを使用するようにㄡあなたの反応コンポーネントを使用してください!
44 |
45 | ```vue
46 |
47 |
48 |
I'm a Vue component
49 |
50 |
51 |
52 |
53 |
68 | ```
69 |
70 | * * *
71 |
72 | vueプラグインとして反応する
73 |
74 | vuera / src / vueplugin.js
75 |
76 | ```js
77 | // Determine if React's component
78 | import isReactComponent from './utils/isReactComponent'
79 | // React Component -> Vue Component
80 | import VueResolver from './resolvers/Vue'
81 |
82 | /**
83 | * vue plugin
84 | */
85 | export default {
86 | install (Vue, options) {
87 | /**
88 | * Custom merge strategy, this strategy is really just
89 | * Wraps all React components, while retaining the Vue components.
90 | */
91 | const originalComponentsMergeStrategy = Vue.config.optionMergeStrategies.components
92 |
93 | Vue.config.optionMergeStrategies.components = function (parent, ... args) {
94 | // value set before <- return Object.assign (parent, wrappedComponents)
95 | const mergedValue = originalComponentsMergeStrategy (parent, ... args)
96 |
97 | //
98 | const wrappedComponents = mergedValue
99 | Object.entries (mergedValue) .reduce (
100 | (acc, [k, v]) => ({
101 | ... acc,
102 | [k]: isReactComponent (v)? VueResolver (v): v,
103 | }),
104 | {}
105 | )
106 | : mergedValue
107 | //merge
108 | return Object.assign (parent, wrappedComponents)
109 | }
110 | },
111 | }
112 | ```
113 |
114 | 登場した
115 |
116 | - [install()](https://cn.vuejs.org/v2/guide/plugins.html#%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6)
117 |
118 | - [vue.configはㄡvueのグローバルコンフィグレーションを含むオブジェクトです. ](https://cn.vuejs.org/v2/api/#optionMergeStrategies)
119 |
120 | - [アプリケーションを起動する前に以下のプロパティを変更することができます: ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)
121 |
122 | ```js
123 | const obj = {foo: 'bar', baz: 42};
124 | console.log (Object.entries (obj)); // [['foo', 'bar'], ['baz', 42]]
125 | ```
126 |
127 | - [object.entries](https://developer.mozilla.org/wiki/Reduce)
128 |
129 | ```js
130 | var flattened = [[0, 1], [2, 3], [4, 5]]. reduce (function (a, b) {
131 | return a.concat (b);
132 | }, []);
133 | // flattened is [0, 1, 2, 3, 4, 5]
134 | ```
135 |
136 | - array.prototype.reduce
137 |
138 | vueresolver
139 |
140 | > vuera / src / resolvers / vue.js
141 |
142 | 反応コンポーネント→vueコンポーネント`あなたはㄡあなたの側を横切ることの使用が`ビュー`コンポーネントが見つかった場合ㄡ`反応する`コンポーネントが変わったように見える`ビュー
143 |
144 | .
145 |
146 | # 第二に言う:
147 |
148 | vueinreact`追加する`ヴェラ/ベルベル`あなたの` `.babelrc`プラグイン
149 |
150 | オプション
151 |
152 | ```json
153 | {
154 | "presets": "react",
155 | "plugins": ["vuera/babel"]
156 | }
157 | ```
158 |
159 | .babelrc
160 |
161 | ```jsx
162 | import React from 'react'
163 | import MyVueComponent from './MyVueComponent.vue'
164 |
165 | export default () => (
166 |
167 |
I'm a react component
168 |
169 |
170 |
171 |
172 | )
173 | ```
174 |
175 | つかいます`追加の方法から`.babelrc
176 |
177 | * * *
178 |
179 | ㄡそれはバベルを使用することは可能ですか?
180 |
181 | ```js
182 | /* eslint-env node */
183 |
184 | function processCreateElement (maybeReactComponent, args, file, path, types) {
185 | // If the first argument is a string (built-in React component), return
186 | if (maybeReactComponent.type === 'StringLiteral') return
187 |
188 | if (! file.insertedVueraImport) {
189 | file.path.node.body.unshift (
190 | types.importDeclaration (
191 | [
192 | types.importSpecifier (
193 | types.identifier ('__ vueraReactResolver'),
194 | types.identifier ('__ vueraReactResolver')
195 | ),
196 | ],
197 | types.stringLiteral ('vuera')
198 | )
199 | )
200 | }
201 | // Prevent duplicate imports
202 | file.insertedVueraImport = true
203 |
204 | // Replace React.createElement (component, props) with our helper function
205 | path.replaceWith (
206 | types.callExpression (types.identifier ('__ vueraReactResolver'), [maybeReactComponent, ... args])
207 | )
208 | }
209 |
210 | // types is the babel plugin API? One of the modules
211 | module.exports = function ({types}) {
212 | return {
213 | visitor: {
214 | CallExpression (path, {file}) {
215 | const callee = path.node.callee
216 | const [maybeReactComponent, ... args] = path.node.arguments
217 |
218 | // If there is a react module, reactImport
219 | const reactImport = file.path.node.body.find (
220 | x => x.type === 'ImportDeclaration' && x.source.value === 'react'
221 | )
222 | if (! reactImport) return
223 |
224 | // if CallExpression is react.createElement
225 | if (callee.type === 'MemberExpression') {
226 | /**
227 | * Get the default import name Examples:
228 | * import React from 'react' => "React"
229 | * import hahaLOL from 'react' => "hahaLOL"
230 | */
231 | const defaultImport = reactImport.specifiers.find (
232 | x => x.type === 'ImportDefaultSpecifier'
233 | )
234 | if (! defaultImport) return
235 | const reactName = defaultImport.local.name
236 |
237 | const {object, property} = callee
238 | if (! (object.name === reactName && property.name === 'createElement')) {
239 | return
240 | }
241 |
242 | processCreateElement (maybeReactComponent, args, file, path, types)
243 | }
244 | // Check CallExpression is react's 'createElement'
245 | if (callee.type === 'Identifier' && callee.name! == '__vueraReactResolver') {
246 | // Return unless createElement was imported
247 | const createElementImport = reactImport.specifiers.find (
248 | x => x.type === 'ImportSpecifier' && x.imported.name === 'createElement'
249 | )
250 | if (! createElementImport) return
251 |
252 | processCreateElement (maybeReactComponent, args, file, path, types)
253 | }
254 | },
255 | },
256 | }
257 | }
258 | ```
259 |
260 | - [vuera / babel.js](https://github.com/babel/babel/tree/master/packages/babel-types)
261 |
262 | - [バベルタイプ](http://web.jobbole.com/88236/)
263 |
264 | - [バベル](https://github.com/thejameskyle/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-api)
265 |
266 | - [バーベル構文プラグイン中国語](http://astexplorer.net/)
267 |
268 | オンラインast構文ツリー`上記のリンクはあなたがシンプルで明確になるのを助けることができます` `バベル`プラグイン`プラグイン`importspecifier
269 |
270 | > メンバーの問題?`一般的にbabel.jsはㄡ`react.createelement``\`\_\_vuerareactresolver
271 |
272 | ```js
273 | export function babelReactResolver (component, props, children) {
274 | return isReactComponent (component)
275 | ? React.createElement (component, props, children)
276 | : React.createElement (VueWrapper, Object.assign ({component}, props), children)
277 | }
278 |
279 | // babelReactResolver as __vueraReactResolver
280 | ```
281 |
282 | 組み込み関数
283 |
284 | * * *
285 |
286 | 上記はバベルがjsをダウングレードするために行う最後のものです
287 |
288 | vuewrapper上記のコードが最も重要です
289 |
290 | ```js
291 | import React from 'react'
292 | import Vue from 'vue'
293 | import ReactWrapper from './React'
294 |
295 | const VUE_COMPONENT_NAME = 'vuera-internal-component-name'
296 |
297 | const wrapReactChildren = (createElement, children) =>
298 | createElement ('vuera-internal-react-wrapper', {
299 | props: {
300 | component: () => {children}
,
301 | },
302 | })
303 |
304 | export default class VueContainer extends React.Component {
305 | constructor (props) {
306 | super (props)
307 |
308 | /**
309 | * Incoming and redefinition of real components
310 | * `component` prop.
311 | */
312 | this.currentVueComponent = props.component
313 |
314 | /**
315 | * Modify the createVueInstance function to pass this binding correctly. Doing this
316 | * Constructor Avoid rendering functions in the rendering.
317 | *// I feel a bit difficult to understand: translator said
318 | */
319 | const createVueInstance = this.createVueInstance
320 | const self = this
321 | this.createVueInstance = function (element, component, prevComponent) {
322 | createVueInstance (element, self, component, prevComponent)
323 | }
324 | }
325 |
326 | componentWillReceiveProps (nextProps) {
327 | const {component, ... props} = nextProps
328 |
329 | if (this.currentVueComponent! == component) {
330 | this.updateVueComponent (this.props.component, component)
331 | }
332 | /**
333 | * NOTE: Did not compare props and nextprops, because I did not want to write a
334 | function for deep object comparison. I do not know if this hurts performance a lot, maybe
335 | * we do need to compare those objects.
336 | */
337 | Object.assign (this.vueInstance. $ Data, props)
338 | }
339 |
340 | componentWillUnmount () {
341 | this.vueInstance. $ destroy ()
342 | }
343 |
344 | /**
345 | * Create and load VueInstance interface
346 | * NOTE: VueInstance inside VueContainer
347 | * We can not bind createVueInstance to this VueContainer object and need to be explicit
348 | Pass the binding object
349 | * @param {HTMLElement} targetElement - element to attact the Vue instance to
350 | * @param {ReactInstance} reactThisBinding - current instance of VueContainer
351 | */
352 | createVueInstance (targetElement, reactThisBinding) {
353 | const {component, ... props} = reactThisBinding.props
354 |
355 | // `this` refers to Vue instance in the constructor
356 | reactThisBinding.vueInstance = new Vue ({
357 | // targetElement is VueContainer render () element
358 | el: targetElement,
359 | data: props,
360 | render (createElement) {
361 | return createElement (
362 | VUE_COMPONENT_NAME,
363 | {
364 | props: this. $ data,
365 | },
366 | [wrapReactChildren (createElement, this.children)]
367 | )
368 | },
369 | components: {
370 | [VUE_COMPONENT_NAME]: component,
371 | 'vuera-internal-react-wrapper': ReactWrapper,
372 | },
373 | })
374 | }
375 |
376 | updateVueComponent (prevComponent, nextComponent) {
377 | this.currentVueComponent = nextComponent
378 |
379 | /**
380 | * Replace the component in the Vue instance and update it.
381 | */
382 | this.vueInstance. $ options.components [VUE_COMPONENT_NAME] = nextComponent
383 | this.vueInstance. $ forceUpdate ()
384 | }
385 |
386 | render () {
387 | return
388 | }
389 | }
390 | ```
391 |
392 | vuera / src / wrapper / vue.js`その`props.component`コンポーネントが`react.component`ㄡ次に`vue instance reactthisbinding.vueinstance
393 |
394 | - 内部で作成されます. `これと同じことはㄡ`<div ref = {this.createvueinstance} />
395 |
396 | * * *
397 |
398 | ## vue react.componentコンポーネントのイベントハンドラ呼び出しの一部に渡します.
399 |
400 | ### 続きを見るisreactcomponent
401 |
402 | ```js
403 | export default function isReactComponent (component) {
404 | if (typeof component === 'object') {
405 | return false // no object == no react
406 | } else if (
407 | typeof component === 'function' &&
408 | component.prototype.constructor.super &&
409 | component.prototype.constructor.super.name.startsWith('Vue')
410 | ) {
411 | return false // is vue
412 | } else {
413 | return true // is react
414 | }
415 | }
416 | ```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vuera
2 |
3 | [](https://github.com/chinanf-boy/Source-Explain)
4 |
5 | Explanation
6 | > "version": "0.1.3"
7 |
8 | [github source](https://github.com/topics/vuera)
9 |
10 | [中文版](./README.zh.md)
11 |
12 | [日文版](./README.ja.md)
13 |
14 | ---
15 |
16 | Run in react and vue at the same time.
17 |
18 | - [react on vue](#ReactInVue)
19 |
20 | - [vue at react](#VueInReact)
21 |
22 | ---
23 |
24 | - see more
25 |
26 | - [isReactComponent](#isreactcomponent)
27 |
28 | ---
29 |
30 | Two situations
31 |
32 | First said first:
33 |
34 | # ReactInVue
35 |
36 | ```js
37 | import Vue from 'vue'
38 | import {VuePlugin} from 'vuera'
39 |
40 | Vue.use (VuePlugin)
41 | /* ... */
42 | ```
43 |
44 | Now, use your React components like you will normally use your Vue components!
45 |
46 | ```vue
47 |
48 |
49 |
I'm a Vue component
50 |
51 |
52 |
53 |
54 |
69 | ```
70 |
71 | ---
72 |
73 | Use React as a Vue plugin
74 |
75 | vuera/src/VuePlugin.js
76 | ```js
77 | // Determine if React's component
78 | import isReactComponent from './utils/isReactComponent'
79 | // React Component -> Vue Component
80 | import VueResolver from './resolvers/Vue'
81 |
82 | /**
83 | * vue plugin
84 | */
85 | export default {
86 | install (Vue, options) {
87 | /**
88 | * Custom merge strategy, this strategy is really just
89 | * Wraps all React components, while retaining the Vue components.
90 | */
91 | const originalComponentsMergeStrategy = Vue.config.optionMergeStrategies.components
92 |
93 | Vue.config.optionMergeStrategies.components = function (parent, ... args) {
94 | // value set before <- return Object.assign (parent, wrappedComponents)
95 | const mergedValue = originalComponentsMergeStrategy (parent, ... args)
96 |
97 | //
98 | const wrappedComponents = mergedValue
99 | Object.entries (mergedValue) .reduce (
100 | (acc, [k, v]) => ({
101 | ... acc,
102 | [k]: isReactComponent (v)? VueResolver (v): v,
103 | }),
104 | {}
105 | )
106 | : mergedValue
107 | //merge
108 | return Object.assign (parent, wrappedComponents)
109 | }
110 | },
111 | }
112 |
113 | ```
114 |
115 | Which appeared
116 |
117 | - [install ()](https://cn.vuejs.org/v2/guide/plugins.html#%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6)
118 |
119 | - [vue.config is an object that contains the global configuration of Vue. You can modify the following properties before starting the application:](https://cn.vuejs.org/v2/api/#optionMergeStrategies)
120 |
121 | - [Object.entries](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)
122 |
123 | ```js
124 | const obj = {foo: 'bar', baz: 42};
125 | console.log (Object.entries (obj)); // [['foo', 'bar'], ['baz', 42]]
126 | ```
127 | - [Array.prototype.reduce](https://developer.mozilla.org/wiki/Reduce)
128 |
129 | ```js
130 | var flattened = [[0, 1], [2, 3], [4, 5]]. reduce (function (a, b) {
131 | return a.concat (b);
132 | }, []);
133 | // flattened is [0, 1, 2, 3, 4, 5]
134 | ```
135 |
136 | - VueResolver
137 |
138 | vuera/src/resolvers/Vue.js
139 | > React Component -> Vue Component
140 |
141 | So you can see that the use of traversing the side of the `` Vue`` component, if found, `` React`` component looks like it is transformed into `` Vue``.
142 |
143 | Say the second:
144 |
145 | # VueInReact
146 |
147 | Add `vuera/babel` to your` .babelrc` `plugins` option
148 |
149 | .babelrc
150 | ```json
151 | {
152 | "presets": "react",
153 | "plugins": ["vuera/babel"]
154 | }
155 | ```
156 |
157 | use
158 |
159 | ```jsx
160 | import React from 'react'
161 | import MyVueComponent from './MyVueComponent.vue'
162 |
163 | export default () => (
164 |
165 |
I'm a react component
166 |
167 |
168 |
169 |
170 | )
171 | ```
172 |
173 | From the way of adding `` .babelrc``, is it possible to use babel?
174 |
175 | ---
176 |
177 | vuera/babel.js
178 | ```js
179 | /* eslint-env node */
180 |
181 | function processCreateElement (maybeReactComponent, args, file, path, types) {
182 | // If the first argument is a string (built-in React component), return
183 | if (maybeReactComponent.type === 'StringLiteral') return
184 |
185 | if (! file.insertedVueraImport) {
186 | file.path.node.body.unshift (
187 | types.importDeclaration (
188 | [
189 | types.importSpecifier (
190 | types.identifier ('__ vueraReactResolver'),
191 | types.identifier ('__ vueraReactResolver')
192 | ),
193 | ],
194 | types.stringLiteral ('vuera')
195 | )
196 | )
197 | }
198 | // Prevent duplicate imports
199 | file.insertedVueraImport = true
200 |
201 | // Replace React.createElement (component, props) with our helper function
202 | path.replaceWith (
203 | types.callExpression (types.identifier ('__ vueraReactResolver'), [maybeReactComponent, ... args])
204 | )
205 | }
206 |
207 | // types is the babel plugin API? One of the modules
208 | module.exports = function ({types}) {
209 | return {
210 | visitor: {
211 | CallExpression (path, {file}) {
212 | const callee = path.node.callee
213 | const [maybeReactComponent, ... args] = path.node.arguments
214 |
215 | // If there is a react module, reactImport
216 | const reactImport = file.path.node.body.find (
217 | x => x.type === 'ImportDeclaration' && x.source.value === 'react'
218 | )
219 | if (! reactImport) return
220 |
221 | // if CallExpression is react.createElement
222 | if (callee.type === 'MemberExpression') {
223 | /**
224 | * Get the default import name Examples:
225 | * import React from 'react' => "React"
226 | * import hahaLOL from 'react' => "hahaLOL"
227 | */
228 | const defaultImport = reactImport.specifiers.find (
229 | x => x.type === 'ImportDefaultSpecifier'
230 | )
231 | if (! defaultImport) return
232 | const reactName = defaultImport.local.name
233 |
234 | const {object, property} = callee
235 | if (! (object.name === reactName && property.name === 'createElement')) {
236 | return
237 | }
238 |
239 | processCreateElement (maybeReactComponent, args, file, path, types)
240 | }
241 | // Check CallExpression is react's 'createElement'
242 | if (callee.type === 'Identifier' && callee.name! == '__vueraReactResolver') {
243 | // Return unless createElement was imported
244 | const createElementImport = reactImport.specifiers.find (
245 | x => x.type === 'ImportSpecifier' && x.imported.name === 'createElement'
246 | )
247 | if (! createElementImport) return
248 |
249 | processCreateElement (maybeReactComponent, args, file, path, types)
250 | }
251 | },
252 | },
253 | }
254 | }
255 |
256 | ```
257 |
258 | - [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types)
259 |
260 | - [babel-AST](http://web.jobbole.com/88236/)
261 |
262 | - [babel-syntax plug-in Chinese](https://github.com/thejameskyle/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-api)
263 |
264 | - [Online AST Syntax Tree](http://astexplorer.net/)
265 |
266 | This part of the need to understand the AST syntax tree, the above link can help you to be simple and clear `babel` `` plugins`` plug-in ` ImportSpecifier`member problems?
267 |
268 | > babel.js in general, is to turn `` react.createElement`` into a `__vueraReactResolver`` built-in function
269 |
270 | ```js
271 | export function babelReactResolver (component, props, children) {
272 | return isReactComponent (component)
273 | ? React.createElement (component, props, children)
274 | : React.createElement (VueWrapper, Object.assign ({component}, props), children)
275 | }
276 |
277 | // babelReactResolver as __vueraReactResolver
278 | ```
279 |
280 | The above is only the last thing babel does to downgrade js
281 |
282 | ---
283 |
284 | VueWrapper The above code is the most important
285 |
286 | vuera/src/wrapper/vue.js
287 | ```js
288 | import React from 'react'
289 | import Vue from 'vue'
290 | import ReactWrapper from './React'
291 |
292 | const VUE_COMPONENT_NAME = 'vuera-internal-component-name'
293 |
294 | const wrapReactChildren = (createElement, children) =>
295 | createElement ('vuera-internal-react-wrapper', {
296 | props: {
297 | component: () => {children}
,
298 | },
299 | })
300 |
301 | export default class VueContainer extends React.Component {
302 | constructor (props) {
303 | super (props)
304 |
305 | /**
306 | * Incoming and redefinition of real components
307 | * `component` prop.
308 | */
309 | this.currentVueComponent = props.component
310 |
311 | /**
312 | * Modify the createVueInstance function to pass this binding correctly. Doing this
313 | * Constructor Avoid rendering functions in the rendering.
314 | *// I feel a bit difficult to understand: translator said
315 | */
316 | const createVueInstance = this.createVueInstance
317 | const self = this
318 | this.createVueInstance = function (element, component, prevComponent) {
319 | createVueInstance (element, self, component, prevComponent)
320 | }
321 | }
322 |
323 | componentWillReceiveProps (nextProps) {
324 | const {component, ... props} = nextProps
325 |
326 | if (this.currentVueComponent! == component) {
327 | this.updateVueComponent (this.props.component, component)
328 | }
329 | /**
330 | * NOTE: Did not compare props and nextprops, because I did not want to write a
331 | function for deep object comparison. I do not know if this hurts performance a lot, maybe
332 | * we do need to compare those objects.
333 | */
334 | Object.assign (this.vueInstance. $ Data, props)
335 | }
336 |
337 | componentWillUnmount () {
338 | this.vueInstance. $ destroy ()
339 | }
340 |
341 | /**
342 | * Create and load VueInstance interface
343 | * NOTE: VueInstance inside VueContainer
344 | * We can not bind createVueInstance to this VueContainer object and need to be explicit
345 | Pass the binding object
346 | * @param {HTMLElement} targetElement - element to attact the Vue instance to
347 | * @param {ReactInstance} reactThisBinding - current instance of VueContainer
348 | */
349 | createVueInstance (targetElement, reactThisBinding) {
350 | const {component, ... props} = reactThisBinding.props
351 |
352 | // `this` refers to Vue instance in the constructor
353 | reactThisBinding.vueInstance = new Vue ({
354 | // targetElement is VueContainer render () element
355 | el: targetElement,
356 | data: props,
357 | render (createElement) {
358 | return createElement (
359 | VUE_COMPONENT_NAME,
360 | {
361 | props: this. $ data,
362 | },
363 | [wrapReactChildren (createElement, this.children)]
364 | )
365 | },
366 | components: {
367 | [VUE_COMPONENT_NAME]: component,
368 | 'vuera-internal-react-wrapper': ReactWrapper,
369 | },
370 | })
371 | }
372 |
373 | updateVueComponent (prevComponent, nextComponent) {
374 | this.currentVueComponent = nextComponent
375 |
376 | /**
377 | * Replace the component in the Vue instance and update it.
378 | */
379 | this.vueInstance. $ options.components [VUE_COMPONENT_NAME] = nextComponent
380 | this.vueInstance. $ forceUpdate ()
381 | }
382 |
383 | render () {
384 | return
385 | }
386 | }
387 | ```
388 |
389 | The `` props.component`` component is wrapped with `` React.Component``, and then the `` Vue instance reactThisBinding.vueInstance`` is created internally.
390 |
391 | - Equivalent to say that this part of the `` `` to the Vue processing, and then part of Vue react.component component event handler call.
392 |
393 | ---
394 |
395 | ## see more
396 |
397 | ### isReactComponent
398 |
399 | ``` js
400 | export default function isReactComponent (component) {
401 | if (typeof component === 'object') {
402 | return false // no object == no react
403 | } else if (
404 | typeof component === 'function' &&
405 | component.prototype.constructor.super &&
406 | component.prototype.constructor.super.name.startsWith('Vue')
407 | ) {
408 | return false // is vue
409 | } else {
410 | return true // is react
411 | }
412 | }
413 |
414 | ```
--------------------------------------------------------------------------------
/minilogo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------