如何使用 React/Angular/Vue 使 </div> 更接近其 <div>?
How to bring a </div> closer to its <div> using React/Angular/Vue?
序曲
经验丰富的 C(比方说)开发人员看到一个 200 行的函数会感到不安。他们将努力将其分为(比如说)十个功能,每个功能 20 行。笼统地说,objective 是一个永远不会跨越一个屏幕的功能。
上下文(更新)
如果桌面(比如 C 语言)应用程序是模块化编写的,则可以轻松定位错误。查看 main
调用的所有函数。找出罪魁祸首;看看里面;递归。相比之下 HTML 开发是一场噩梦。当发现缺陷或需要改进时,很难缩小应该修改的范围。是否有比服务器端模板或客户端库更好的模块化工具? React/Angular/Vue适合这份工作吗?怎么样?
动机
Web 开发人员面临同样的问题。开盘 <div>
和相应的闭盘 </div>
之间的线永远不要跨越一个屏幕是可取的。如果不能在一个屏幕上同时看到两者,就很难在脑海中保持良好的代码形象。
模板引擎,比如 Jinja,可用于拆分 HTML 文件。
例如,有一个文件
<div class="main">
<div class="left">
<!-- many lines -->
</div>
<div class="right">
<!-- many lines -->
</div>
</div>
Jinja 的推导可用于将 HTML 文件拆分为父文件和子文件。
<div class="main">
{% block left %}
{% endblock %}
{% block right %}
{% endblock %}
</div>
{% extends "main.html" %}
{% block left %}
<!-- many lines -->
{% endblock %}
{% block right %}
<!-- many lines -->
{% endblock %}
这让人想起在 C 语言中拆分函数的方式。长函数中丑陋的标志是缩进 也 变得过多,然后也很难准确地看到循环开始和结束的位置。
D3.js同样可以使用。上面的HTML文件变成一对HTML和JS文件
<div class="main">
</div>
let main = d3.select("main")
let left = main.append("div")
.attr("class", "left");
let right = main.append("div")
.attr("class", "right");
(或者,对于此 objective 的工业级 D3 使用,请参阅 here。)
问题
这些解决方案都不合适。他们喜欢使用一个强大的工具来做一些非常简单的事情,而代码的新手(或离开后的自己)不会清晰地看到一些几乎微不足道的东西。
如何使用 React/Angular/Vue
拆分上面的第一个 HTML 文件,以确保 <div class="main">
和它的结尾 </div>
无论如何都只分开几行左右 DIV 中有多少代码?
我发现许多旧的和现代的框架都有同样的问题,它们没有考虑组件和组件重用。特别是,如果您想要包含数据元素,并假设您希望 left
是邮寄地址,right
是送货地址,那么创建像 [=13= 这样的通用组件是非常困难的] 同时用于同一表格中的两者,并稳健地处理数据。
您可以按顺序进行,例如对于 name="street"
的输入,数据将是一个数组,左侧可能是 [0]
,右侧可能是 [1]
,但是您必须小心处理空值,如果重新设计表单,数据可能不会到达更新操作的正确位置。
我知道它很古老,但我发现 ExtJS 是处理组件和处理数据的非常好的框架。相当于您的 HTML 可能是:
{
name: 'main',
xtype: 'left-and-right',
leftItem: {
name: 'mailing'
xtype: 'address'
},
rightItem: {
name: 'shipping',
xtype: 'address'
}
}
显然,传播需要更多的智慧name
,但所有这些都是在幕后处理的,因此您可以参考mailing.street
或shipping.street
而不必担心关于空值和表单重新设计。
不幸的是,ExtJS 基本上是垂死的,现在由一家因各种错误原因而闻名的公司所有,所有相关开发人员都离开了,所以不建议尝试这条路。
Google Polymer 的方向是正确的,并且它与框架无关,因此它可能是与任何现代框架一起使用的不错选择。同样,如果您将其用于数据,则可能需要构建智能来传播 name
.
你的问题很周到。我觉得我的回答有点简单,所以我可能误解了你提问的目的。
React 和 Vue 利用组件来组织模板并抽象出 DOM 的复杂性。您可以在这些组件中封装任意多的行为。
从 Vue POV(虽然 React 有类似的工具来实现相同的目标)......如果你想要更少的代码行,你可以封装整个 "left" 和 "right"(它们的行为、样式和模板)作为组件*。如果两个组件不共享任何状态或数据,这将变得非常简单明了,因为您不需要传递任何道具。
代码示例(在 Vue 中)
这是一个过于简单的示例,通常 MyPage.vue
会有其他行为,例如加载、动画或设置初始应用程序的数据。
// MyPage.vue
<template>
<div>
<v-left/>
<v-right/>
</div>
</template>
<script>
import VRight from '@/components/right.vue';
import VLeft from '@/components/left.vue';
export default {
components: {
VRight,
VLeft
}
}
</script>
最后,Vue 术语可能涵盖得太多了,但是 this document.
中有一些很好的代码组织示例(适用于任何语言或框架)
代码复杂性的衡量标准不仅在于给定函数中的代码行数,还在于实现某事时需要接触的地方数量或破坏代码其他区域的风险。
前端代码最脆弱的区域之一是 CSS,这就是为什么用组件打包 CSS(使用 CSS-in-JS 方法或使用 Vue 的作用域styles) 大大提高了您编写的代码的稳定性和可重用性。
希望对您有所帮助!!
*(在很多情况下这样做并不是最佳选择是有原因的)
"How can the first HTML file above be split using React/Angular/Vue to make sure that and its closing remain only a few lines apart no matter how much code goes into the left and right DIVs?"
JSX(XML 编译成创建虚拟 DOM 元素的 JS 代码的方言)很好地实现了这一点。它在 React 中使用,有时在 Vue 中使用。
以使用许多函数的 C 示例为基础,这正是我们在这里所做的。每个 JSX 标签代表一个 returns 另一个函数或一些 UI 元素的函数。我们可以将 <App />
拆分为 <Main />
、<Left />
和 <Right />
函数,而不是将所有内容都放在 <App />
中:
const App = props => (
<Main>
<Left />
<Right />
</Main>
)
ReactDOM.render('.app', <App />);
回答主要问题,这是这些框架存在的理由之一吗?恕我直言,不,它只是用于表达创建 vdom 元素的调用的 shorthand,因为编写 <App />
比到处嵌套 React.creatElement({...etc})
更容易。因为它恰好代表函数,所以它确实符合你在序言中提到的想法。但我认为它只是为了方便创建 vdom 而创建的 elements/HTML-in-JS,而不是明确作为模板引擎(其中有很多!)
如果我理解正确你的问题,我想你可以在使用 Svelte 之后得到你想要的东西,它与 React/Vue 有相似的目标,但在某些方面更像是一个组件编译器而不是一个框架。
官方 tutorial 上手非常快 read/play 并且教授几乎所有知识。
在你的情况下你可以看看这个playground:
App.svelte - 主应用程序文件
<script>
import Main from './Main.svelte';
import Left from './Left.svelte';
import Right from './Right.svelte';
</script>
<Main>
<Left />
<Right />
</Main>
Main.svelte - <Main>
组件(<slot>
表示来自组件调用者的内容 - 在本例中它包含 "calls" 到 Left 和 Right)
<style>
...
</style>
<div class="main">
<slot></slot>
</div>
Left.svelte - <Left>
分量
<script>
import Box from './Box.svelte';
</script>
<Box title="Left">
<p>
...
</p>
</Box>
Right.svelte 本质上等于 Left.svelte
Box.svelte - <Left>
和 <Right>
都使用的公共 <Box>
内部组件
<script>
export let title = "box";
</script>
<style>
...
</style>
<div class="box">
<h3>
{title}
</h3>
<slot></slot>
</div>
(这里把Left和Right共同的部分分解成一个Box组件,但是这显然是没有必要的)
为了完整起见,把例子变成一个完整的程序:
main.js
import App from './App.svelte';
const app = new App({
target: document.body,
});
export default app;
package.json
{
"name": "svelte-demo",
"version": "1.0.0",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public"
},
"devDependencies": {
"rollup": "^1.12.0",
"rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-livereload": "^1.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^5.1.2",
"svelte": "^3.0.0"
},
"dependencies": {
"sirv-cli": "^0.4.4"
}
}
rollup.config.js:
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file — better for performance
css: css => {
css.write('public/build/bundle.css');
}
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration —
// consult the documentation for details:
// https://github.com/rollup/rollup-plugin-commonjs
resolve({
browser: true,
dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/')
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
然后:
npm install
npm run dev
(请注意,这实际上是官方 svelte templeate 使用 npx degit sveltejs/template my-svelte-project
创建的内容,如着陆页所述)
好消息是 Svelte 编译为标准 JavaScript 类,因此可以使用来自正则化 JavaScript 的组件。从这个角度来看,它是一个无框架框架。参见示例 this article
序曲
经验丰富的 C(比方说)开发人员看到一个 200 行的函数会感到不安。他们将努力将其分为(比如说)十个功能,每个功能 20 行。笼统地说,objective 是一个永远不会跨越一个屏幕的功能。
上下文(更新)
如果桌面(比如 C 语言)应用程序是模块化编写的,则可以轻松定位错误。查看 main
调用的所有函数。找出罪魁祸首;看看里面;递归。相比之下 HTML 开发是一场噩梦。当发现缺陷或需要改进时,很难缩小应该修改的范围。是否有比服务器端模板或客户端库更好的模块化工具? React/Angular/Vue适合这份工作吗?怎么样?
动机
Web 开发人员面临同样的问题。开盘 <div>
和相应的闭盘 </div>
之间的线永远不要跨越一个屏幕是可取的。如果不能在一个屏幕上同时看到两者,就很难在脑海中保持良好的代码形象。
模板引擎,比如 Jinja,可用于拆分 HTML 文件。
例如,有一个文件
<div class="main">
<div class="left">
<!-- many lines -->
</div>
<div class="right">
<!-- many lines -->
</div>
</div>
Jinja 的推导可用于将 HTML 文件拆分为父文件和子文件。
<div class="main">
{% block left %}
{% endblock %}
{% block right %}
{% endblock %}
</div>
{% extends "main.html" %}
{% block left %}
<!-- many lines -->
{% endblock %}
{% block right %}
<!-- many lines -->
{% endblock %}
这让人想起在 C 语言中拆分函数的方式。长函数中丑陋的标志是缩进 也 变得过多,然后也很难准确地看到循环开始和结束的位置。
D3.js同样可以使用。上面的HTML文件变成一对HTML和JS文件
<div class="main">
</div>
let main = d3.select("main")
let left = main.append("div")
.attr("class", "left");
let right = main.append("div")
.attr("class", "right");
(或者,对于此 objective 的工业级 D3 使用,请参阅 here。)
问题
这些解决方案都不合适。他们喜欢使用一个强大的工具来做一些非常简单的事情,而代码的新手(或离开后的自己)不会清晰地看到一些几乎微不足道的东西。
如何使用 React/Angular/Vue
拆分上面的第一个 HTML 文件,以确保 <div class="main">
和它的结尾 </div>
无论如何都只分开几行左右 DIV 中有多少代码?
我发现许多旧的和现代的框架都有同样的问题,它们没有考虑组件和组件重用。特别是,如果您想要包含数据元素,并假设您希望 left
是邮寄地址,right
是送货地址,那么创建像 [=13= 这样的通用组件是非常困难的] 同时用于同一表格中的两者,并稳健地处理数据。
您可以按顺序进行,例如对于 name="street"
的输入,数据将是一个数组,左侧可能是 [0]
,右侧可能是 [1]
,但是您必须小心处理空值,如果重新设计表单,数据可能不会到达更新操作的正确位置。
我知道它很古老,但我发现 ExtJS 是处理组件和处理数据的非常好的框架。相当于您的 HTML 可能是:
{
name: 'main',
xtype: 'left-and-right',
leftItem: {
name: 'mailing'
xtype: 'address'
},
rightItem: {
name: 'shipping',
xtype: 'address'
}
}
显然,传播需要更多的智慧name
,但所有这些都是在幕后处理的,因此您可以参考mailing.street
或shipping.street
而不必担心关于空值和表单重新设计。
不幸的是,ExtJS 基本上是垂死的,现在由一家因各种错误原因而闻名的公司所有,所有相关开发人员都离开了,所以不建议尝试这条路。
Google Polymer 的方向是正确的,并且它与框架无关,因此它可能是与任何现代框架一起使用的不错选择。同样,如果您将其用于数据,则可能需要构建智能来传播 name
.
你的问题很周到。我觉得我的回答有点简单,所以我可能误解了你提问的目的。
React 和 Vue 利用组件来组织模板并抽象出 DOM 的复杂性。您可以在这些组件中封装任意多的行为。
从 Vue POV(虽然 React 有类似的工具来实现相同的目标)......如果你想要更少的代码行,你可以封装整个 "left" 和 "right"(它们的行为、样式和模板)作为组件*。如果两个组件不共享任何状态或数据,这将变得非常简单明了,因为您不需要传递任何道具。
代码示例(在 Vue 中)
这是一个过于简单的示例,通常 MyPage.vue
会有其他行为,例如加载、动画或设置初始应用程序的数据。
// MyPage.vue
<template>
<div>
<v-left/>
<v-right/>
</div>
</template>
<script>
import VRight from '@/components/right.vue';
import VLeft from '@/components/left.vue';
export default {
components: {
VRight,
VLeft
}
}
</script>
最后,Vue 术语可能涵盖得太多了,但是 this document.
中有一些很好的代码组织示例(适用于任何语言或框架)代码复杂性的衡量标准不仅在于给定函数中的代码行数,还在于实现某事时需要接触的地方数量或破坏代码其他区域的风险。
前端代码最脆弱的区域之一是 CSS,这就是为什么用组件打包 CSS(使用 CSS-in-JS 方法或使用 Vue 的作用域styles) 大大提高了您编写的代码的稳定性和可重用性。
希望对您有所帮助!!
*(在很多情况下这样做并不是最佳选择是有原因的)
"How can the first HTML file above be split using React/Angular/Vue to make sure that and its closing remain only a few lines apart no matter how much code goes into the left and right DIVs?"
JSX(XML 编译成创建虚拟 DOM 元素的 JS 代码的方言)很好地实现了这一点。它在 React 中使用,有时在 Vue 中使用。
以使用许多函数的 C 示例为基础,这正是我们在这里所做的。每个 JSX 标签代表一个 returns 另一个函数或一些 UI 元素的函数。我们可以将 <App />
拆分为 <Main />
、<Left />
和 <Right />
函数,而不是将所有内容都放在 <App />
中:
const App = props => (
<Main>
<Left />
<Right />
</Main>
)
ReactDOM.render('.app', <App />);
回答主要问题,这是这些框架存在的理由之一吗?恕我直言,不,它只是用于表达创建 vdom 元素的调用的 shorthand,因为编写 <App />
比到处嵌套 React.creatElement({...etc})
更容易。因为它恰好代表函数,所以它确实符合你在序言中提到的想法。但我认为它只是为了方便创建 vdom 而创建的 elements/HTML-in-JS,而不是明确作为模板引擎(其中有很多!)
如果我理解正确你的问题,我想你可以在使用 Svelte 之后得到你想要的东西,它与 React/Vue 有相似的目标,但在某些方面更像是一个组件编译器而不是一个框架。
官方 tutorial 上手非常快 read/play 并且教授几乎所有知识。
在你的情况下你可以看看这个playground:
App.svelte - 主应用程序文件
<script>
import Main from './Main.svelte';
import Left from './Left.svelte';
import Right from './Right.svelte';
</script>
<Main>
<Left />
<Right />
</Main>
Main.svelte - <Main>
组件(<slot>
表示来自组件调用者的内容 - 在本例中它包含 "calls" 到 Left 和 Right)
<style>
...
</style>
<div class="main">
<slot></slot>
</div>
Left.svelte - <Left>
分量
<script>
import Box from './Box.svelte';
</script>
<Box title="Left">
<p>
...
</p>
</Box>
Right.svelte 本质上等于 Left.svelte
Box.svelte - <Left>
和 <Right>
<Box>
内部组件
<script>
export let title = "box";
</script>
<style>
...
</style>
<div class="box">
<h3>
{title}
</h3>
<slot></slot>
</div>
(这里把Left和Right共同的部分分解成一个Box组件,但是这显然是没有必要的)
为了完整起见,把例子变成一个完整的程序:
main.js
import App from './App.svelte';
const app = new App({
target: document.body,
});
export default app;
package.json
{
"name": "svelte-demo",
"version": "1.0.0",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public"
},
"devDependencies": {
"rollup": "^1.12.0",
"rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-livereload": "^1.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^5.1.2",
"svelte": "^3.0.0"
},
"dependencies": {
"sirv-cli": "^0.4.4"
}
}
rollup.config.js:
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file — better for performance
css: css => {
css.write('public/build/bundle.css');
}
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration —
// consult the documentation for details:
// https://github.com/rollup/rollup-plugin-commonjs
resolve({
browser: true,
dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/')
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
然后:
npm install
npm run dev
(请注意,这实际上是官方 svelte templeate 使用 npx degit sveltejs/template my-svelte-project
创建的内容,如着陆页所述)
好消息是 Svelte 编译为标准 JavaScript 类,因此可以使用来自正则化 JavaScript 的组件。从这个角度来看,它是一个无框架框架。参见示例 this article