当代前端应该怎么写这个hello world?

Posted 叶小钗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了当代前端应该怎么写这个hello world?相关的知识,希望对你有一定的参考价值。

前言

大概16年的时候我们队react进行了简单的学习:从DOM操作看Vue&React的前端组件化,顺带补齐React的demo,当时我们只是站在框架角度在学习,随着近几年前端的变化,想写个hello world似乎变得复杂起来,我们今天便一起来看看现代化的前端,应该如何做一个页面,今天我们学习react首先说一下React的体系圈

无论Vue还是React整个体系圈十分的完备,就一个中级前端想要提高自己,完全就可以学习其中一个体系,便可以收获很多东西,从而突破自身

从工程化角度来说,前端脚手架,性能优化,构建等等一系列的工作可以使用webpack处理,这里又会涉及到SSR相关工作,稍微深入一点便会踏进node的领域,可以越挖越深

从前端框架角度来说,如何使用React这种框架解决大型项目的目录设计,小项目拆分,代码组织,UI组件,项目与项目之间的影响,路由、数据流向等等问题处理完毕便会进步很大一步

从大前端角度来说,使用React处理Native领域的问题,使用React兼容小程序的问题,一套代码解决多端运行的策略,比如兼容微信小程序,随便某一点都值得我们研究几个月

从规范来说,我们可以看看React如何组织代码的,测试用例怎么写,怎么维护github,怎么做升级,甚至怎么写文档,都是值得学习的

从后期来说,如何在这个体系上做监控、做日志、做预警,如何让业务与框架更好的融合都是需要思考的

react体系是非常完善的,他不只是一个框架,而是一个庞大的技术体系,优秀的解决方案,基于此,我们十分有必要基于React或者Vue中的一个进行深入学习

也正是因为这个庞大的体系,反而导致我们有时只是想写一个hello world,都变得似乎很困难,于是我们今天就先来使用标准的知识写一个demo试试

文章对应代码地址:https://github.com/yexiaochai/react-demo

演示地址:https://yexiaochai.github.io/react-demo/build/index.html

脚手架

现在的框架已经十分完备了,而且把市场教育的很好,一个框架除了输出源码以外,还需要输出对应脚手架,直接引入框架源文件的做法已经不合适了,如果我们开发react项目,便可以直接使用框架脚手架创建项目,就react来说,暂时这个脚手架create-react-app比较常用,他有以下特点:

① 基本配置为你写好了,如果按照规范来可做到零配置

② 继承了React、JSX、ES6、Flow的支持,这个也是类React框架的标准三件套

③ 因为现在进入了前端编译时代,服务器以及热加载必不可少,一个命令便能运行

首先,我们一个命令安装依赖:

npm install -g create-react-app

然后就可以使用脚手架创建项目了:

create-react-app react-demo
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   └── serviceWorker.js
└── yarn.lock

直接浏览器打开的方法也不适用了,这里开发环境使用一个node服务器,执行代码运行起来:

npm start

系统自动打开一个页面,并且会热更新,看一个项目首先看看其package.json:

{
  "name": "demo",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-scripts": "2.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

所以当我们执行npm run start的时候事实上是执行node_modules/react-script目录下对应脚本,可以看到项目目录本身连webpack的配置文件都没有,所有的配置全部在react-scripts中,如果对工程配置有什么定制化需求,执行

npm run eject

就将node_modules中对应配置拷贝出来了,可随意修改:

config
├── env.js
├── jest
│   ├── cssTransform.js
│   └── fileTransform.js
├── paths.js
├── webpack.config.dev.js
├── webpack.config.prod.js
└── webpackDevServer.config.js
scripts
├── build.js
├── start.js
└── test.js

也可以安装个服务器,可以直接运行build文件中的代码:

npm install -g pushstate-server
pushstate-server build

我们的代码开始比较简单,只写一个hello world就行了,所以把多余的目录文件全部删除之,修改下index.js代码:

├── README.md
├── build
│   ├── asset-manifest.json
│   ├── index.html
│   ├── precache-manifest.ced1e61ba13691d3414ad116326a23a5.js
│   ├── service-worker.js
│   └── static
│       └── js
│           ├── 1.794557b9.chunk.js
│           ├── 1.794557b9.chunk.js.map
│           ├── main.931cdb1a.chunk.js
│           ├── main.931cdb1a.chunk.js.map
│           ├── runtime~main.229c360f.js
│           └── runtime~main.229c360f.js.map
├── config
│   ├── env.js
│   ├── jest
│   │   ├── cssTransform.js
│   │   └── fileTransform.js
│   ├── paths.js
│   ├── webpack.config.js
│   └── webpackDevServer.config.js
├── package.json
├── public
│   └── index.html
├── scripts
│   ├── build.js
│   ├── start.js
│   └── test.js
├── src
│   └── index.js
└── yarn.lock
import React from \'react\';
import ReactDOM from \'react-dom\';

ReactDOM.render(<div>hello world</div>, document.getElementById(\'root\'));

这个代码不难,我想关键是,这个代码写完了,突然就开服务器了,突然就打包成功了,突然就可以运行了,这个对于一些同学有点玄幻,这里就有必要说一下这里的webpack了

webpack

我们说框架的脚手架,其实说白了就是工程化一块的配置,最初几年的工程化主要集中在压缩和优化、到requireJS时代后工程化变得必不可少,当时主要依赖grunt和gulp这类工具,后续为了把重复的工作杀掉工程化就越走越远了,但是和最初其实变化不大,都是一点一点的将各种优化往上加,加之最近两年typescript一击es6新语法需要编译进行,我们就进入了编译时代

webpack已经进入了4.X时代,一般一个团队会有一个同事(可能是架构师)对webpack特别熟悉,将脚手架进行更改后,就可以很长时间不改一下,这个同事有时候主要就做这么一件事情,所以我们偶尔会称他为webpack配置工程师,虽然是个笑话,从侧门也可以看出,webpack至少不是个很容易学习的东西,造成这个情况的原因还不是其本身有多难,主要是最初文档不行,小伙伴想实现一个功能的时候连去哪里找插件,用什么合适的插件只能一个个的试,所以文档是工程化中很重要的一环

这里再简单介绍下webpack,webpack是现在最常用的javascript程序的静态模块打包器(module bundler),他的特点就是以模块(module)为中心,我们只要给一个入口文件,他会根据这个入口文件找到所有的依赖文件,最后捆绑到一起,这里盗个图:

这里几个核心概念是:

① 入口 - 指示webpack应该以哪个模块(一般是个js文件),作为内部依赖图的开始

② 输出 - 告诉将打包后的文件输出到哪里,或者文件名是什么

③ loader - 这个非常关键,这个让webpack能够去处理那些非JavaScript文件,或者是自定义文件,转换为可用的文件,比如将jsx转换为js,将less转换为css

test就是正则标志,标识哪些文件会被处理;use表示用哪个loader 

④ 插件(plugins)

插件被用于转换某些类型的模块,适用于的范围更广,包括打包优化、压缩、重新定义环境中的变量等等,这里举一个小例子进行说明,react中的jsx这种事实上是浏览器直接不能识别的,但是我们却可以利用webpack将之进行一次编译:

// 原 JSX 语法代码
return <h1>Hello,Webpack</h1>

// 被转换成正常的 JavaScript 代码
return React.createElement(\'h1\', null, \'Hello,Webpack\')

这里我们来做个小demo介绍webpack的低阶使用,我们先建立一个文件夹webpack-demo,先建立一个文件src/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
  </body>
</html>

然后我们建立一个js文件src/index.js以及src/data.js以及style.css

import data from \'./data\'
console.log(data);
export default {
    name: \'叶小钗\'
}
* {
    font-size: 16px;
}
.
├── package.json
└── src
    ├── data.js
    ├── index.html
    ├── index.js
    └── style.css

这个时候轮到我们的webpack登场,以及会用到的几个加载器(这里不讲安装过程):

npm install webpack webpack-cli webpack-serve html-webpack-plugin html-loader css-loader style-loader file-loader url-loader --save-dev

① webpack-cli是命令行工具,有了他我们就需要在他的规则下写配置即可,否则我们要自己在node环境写很多文件操作的代码

② loader结尾的都是文件加载器,读取对应的文件需要对应的加载器,比如你自己定义一个.tpl的文件,如果没有现成的loader,你就只能自己写一个

③ 其中还有个node服务器,方便我们调试

因为我们这里的import是es6语法,浏览器不能识别,所以需要安装babel解析语言:

npm install babel-core babel-preset-env babel-loader --save-dev

然后我们在package.json中加入一行代码:

"babel": {
    "presets": ["env"]
}

这个时候就可以创建webpack文件了:

  1 const { resolve } = require(\'path\')
  2 const HtmlWebpackPlugin = require(\'html-webpack-plugin\')
  3 
  4 // 使用 WEBPACK_SERVE 环境变量检测当前是否是在 webpack-server 启动的开发环境中
  5 const dev = Boolean(process.env.WEBPACK_SERVE)
  6 
  7 module.exports = {
  8   /*
  9   webpack 执行模式
 10   development:开发环境,它会在配置文件中插入调试相关的选项,比如 moduleId 使用文件路径方便调试
 11   production:生产环境,webpack 会将代码做压缩等优化
 12   */
 13   mode: dev ? \'development\' : \'production\',
 14 
 15   /*
 16   配置 source map
 17   开发模式下使用 cheap-module-eval-source-map, 生成的 source map 能和源码每行对应,方便打断点调试
 18   生产模式下使用 hidden-source-map, 生成独立的 source map 文件,并且不在 js 文件中插入 source map 路径,用于在 error report 工具中查看 (比如 Sentry)
 19   */
 20   devtool: dev ? \'cheap-module-eval-source-map\' : \'hidden-source-map\',
 21 
 22   // 配置页面入口 js 文件
 23   entry: \'./src/index.js\',
 24 
 25   // 配置打包输出相关
 26   output: {
 27     // 打包输出目录
 28     path: resolve(__dirname, \'dist\'),
 29 
 30     // 入口 js 的打包输出文件名
 31     filename: \'index.js\'
 32   },
 33 
 34   module: {
 35     /*
 36     配置各种类型文件的加载器,称之为 loader
 37     webpack 当遇到 import ... 时,会调用这里配置的 loader 对引用的文件进行编译
 38     */
 39     rules: [
 40       {
 41         /*
 42         使用 babel 编译 ES6 / ES7 / ES8 为 ES5 代码
 43         使用正则表达式匹配后缀名为 .js 的文件
 44         */
 45         test: /\\.js$/,
 46 
 47         // 排除 node_modules 目录下的文件,npm 安装的包不需要编译
 48         exclude: /node_modules/,
 49 
 50         /*
 51         use 指定该文件的 loader, 值可以是字符串或者数组。
 52         这里先使用 eslint-loader 处理,返回的结果交给 babel-loader 处理。loader 的处理顺序是从最后一个到第一个。
 53         eslint-loader 用来检查代码,如果有错误,编译的时候会报错。
 54         babel-loader 用来编译 js 文件。
 55         */
 56         use: [\'babel-loader\', \'eslint-loader\']
 57       },
 58 
 59       {
 60         // 匹配 html 文件
 61         test: /\\.html$/,
 62         /*
 63         使用 html-loader, 将 html 内容存为 js 字符串,比如当遇到
 64         import htmlString from \'./template.html\';
 65         template.html 的文件内容会被转成一个 js 字符串,合并到 js 文件里。
 66         */
 67         use: \'html-loader\'
 68       },
 69 
 70       {
 71         // 匹配 css 文件
 72         test: /\\.css$/,
 73 
 74         /*
 75         先使用 css-loader 处理,返回的结果交给 style-loader 处理。
 76         css-loader 将 css 内容存为 js 字符串,并且会把 background, @font-face 等引用的图片,
 77         字体文件交给指定的 loader 打包,类似上面的 html-loader, 用什么 loader 同样在 loaders 对象中定义,等会下面就会看到。
 78         */
 79         use: [\'style-loader\', \'css-loader\']
 80       }
 81 
 82     ]
 83   },
 84 
 85   /*
 86   配置 webpack 插件
 87   plugin 和 loader 的区别是,loader 是在 import 时根据不同的文件名,匹配不同的 loader 对这个文件做处理,
 88   而 plugin, 关注的不是文件的格式,而是在编译的各个阶段,会触发不同的事件,让你可以干预每个编译阶段。
 89   */
 90   plugins: [
 91     /*
 92     html-webpack-plugin 用来打包入口 html 文件
 93     entry 配置的入口是 js 文件,webpack 以 js 文件为入口,遇到 import, 用配置的 loader 加载引入文件
 94     但作为浏览器打开的入口 html, 是引用入口 js 的文件,它在整个编译过程的外面,
 95     所以,我们需要 html-webpack-plugin 来打包作为入口的 html 文件
 96     */
 97     new HtmlWebpackPlugin({
 98       /*
 99       template 参数指定入口 html 文件路径,插件会把这个文件交给 webpack 去编译,
100       webpack 按照正常流程,找到 loaders 中 test 条件匹配的 loader 来编译,那么这里 html-loader 就是匹配的 loader
101       html-loader 编译后产生的字符串,会由 html-webpack-plugin 储存为 html 文件到输出目录,默认文件名为 index.html
102       可以通过 filename 参数指定输出的文件名
103       html-webpack-plugin 也可以不指定 template 参数,它会使用默认的 html 模板。
104       */
105       template: \'./src/index.html\',
106 
107       /*
108       因为和 webpack 4 的兼容性问题,chunksSortMode 参数需要设置为 none
109       https://github.com/jantimon/html-webpack-plugin/issues/870
110       */
111       chunksSortMode: \'none\'
112     })
113   ]
114 }
webpack.config.js

然后执行webpack命令便构建好了我们的文件:

.
├── dist
│   ├── index.html
│   ├── index.js
│   └── index.js.map
├── package-lock.json
├── package.json
├── src
│   ├── data.js
│   ├── index.html
│   ├── index.js
│   └── style.css
└── webpack.config.js

可以看到,只要找到我们的入口文件index.js,便能轻易的将所有的模块打包成一个文件,包括样式文件,我们关于webpack的介绍到此为止,更详细的介绍请看这里:https://juejin.im/entry/5b63eb8bf265da0f98317441

我们脚手架中的webpack配置实现相对比较复杂,我们先学会基本使用,后面点再来怎么深入这块,因为现有的配置肯定不能满足我们项目的需求

页面实现

这里为了更多的解决大家工作中会遇到到问题,我们这里实现两个页面:

① 首页,包括城市列表选择页面

② 列表页面,并且会实现滚动刷新等效果

页面大概长这个样子(因为这个页面之前我就实现过,所以样式部分我便直接拿过来使用即可,大家关注逻辑实现即可):

我们这里先捡硬骨头坑,直接就来实现这里的列表页面,这里是之前的页面,大家可以点击对比看看

组件拆分

react两个核心第一是摆脱dom操作,第二是组件化开发,这两点在小型项目中意义都不是十分大,只有经历过多人维护的大项目,其优点才会体现出来,我们这里第一步当然也是拆分页面

这里每一个模块都是一个组件,从通用性来说我们可以将之分为:

① UI组件,与业务无关的组件,只需要填充数据,比如这里的header组件和日历组件以及其中的列表模块也可以分离出一个组件,但看业务耦合大不大

② 页面组件,页面中的元素

工欲善其事必先利其器,所以我们这里先来实现几个组件模块,这里首先是对于新人比较难啃的日历模块,我们代码过程中也会给大家说目录该如何划分

日历组件

日了组件是相对比较复杂的组件了,单单这个组件又可以分为:

① 月组件,处理月部分

② 日部分,处理日期部分

能够将这个组件做好,基本对组件系统会有个初步了解了,我们这里首先来实现日历-日部分,这里我们为项目建立一个src/ui/calendar目录,然后创建我们的文件:

.
├── index.js
└── ui
    └── calendar
        └── calendar.js
import React from \'react\';
import ReactDOM from \'react-dom\';
import Calendar from \'./ui/calendar/calendar\';

ReactDOM.render(<Calendar/>, document.getElementById(\'root\'));
import React from \'react\';

export default class Calendar extends React.Component {

    render() {
        return (
            <div>日历</div>
        )
    }
}

这个时候再执行以下命令便会编译运行:

npm run start

虽然不知为什么,但是我们的代码运行了,大概就是这么一个情况:),接下来我们开始来完善我们的代码,日历组件,我们外层至少得告诉日历年和月,日历才好做展示,那么这里出现了第一个问题,我们怎么将属性数据传给组件呢?这里我们来简单描述下react中的state与props

state是react中的状态属性,定义一个正确的状态是写组件的第一步,state需要代表组件UI的完整状态集,任何UI的改变都应该从state体现出来,判断组件中一个变量是不是该作为state有以下依据:

① 这个变量是否是从父组件获取,如果是,那么他应该是一个属性

② 这个变量是否在组件的整个生命周期不会变化,如果是,那么他也是个属性

③ 这个变量是否是通过其他状态或者属性计算出来的,如果是,那么他也不是一个状态

④ 状态需要在组件render时候被用到

这里的主要区别是state是可变的,而props是只读的,如果想要改变props,只能通过父组件修改,就本章内容,我们将年月等设置为属性,这里先忽略样式的处理,简单几个代码,轮廓就出来了,这里有以下变化:

① 新增common文件夹,放了工具类函数

② 新增static目录存放css,这里的css我们后续会做特殊处理,这里先不深入

于是,我们目录变成了这样:

.
├── README.md
├── package-lock.json
├── package.json
├── public
│   ├── index.html
│   └── static
│       └── css
│           ├── global.css
│           └── index.css
├── src
│   ├── common
│   │   └── utils.js
│   ├── index.js
│   └── ui
│       └── calendar
│           ├── calendar.js
│           ├── day.js
│           └── month.js

我们将calendar代码贴出来看看:

import React from \'react\';
import dateUtils from \'../../common/utils\'
export default class Calendar extends React.Component {
    render() {
        let year = this.props.year;
        let month = this.props.month;
        let weekDayArr = [\'日\', \'一\', \'二\', \'三\', \'四\', \'五\', \'六\'];
        //获取当前日期数据
        let displayInfo = dateUtils.getDisplayInfo(new Date(year, month, 0));
        return (
            <ul className="cm-calendar ">
                <ul className="cm-calendar-hd">
                    {
                        weekDayArr.map((data, i) => {
                            return <li className="cm-item--disabled">{data}</li>
                        })
                    }
                </ul>
            </ul>
        )
    }
}

样式基本出来了:

这个时候我们需要将月组件实现了,这里贴出来第一阶段的完整代码:

import React from \'react\';
import ReactDOM from \'react-dom\';
import Calendar from \'./ui/calendar/calendar\';

ReactDOM.render(
    <Calendar year="2018" month="12"/>, 
    document.getElementById(\'root\')
);
 1 let isDate = function (date) {
 2     return date && date.getMonth;
 3 };
 4 
 5 //兼容小程序日期
 6 let getDate = function(year, month, day) {
 7     if(!day) day = 1;
 8     return new Date(year, month, day);
 9 }
10 
11 let isLeapYear = function (year) {
12     //传入为时间格式需要处理
13     if (isDate(year)) year = year.getFullYear()
14     if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true;
15     return false;
16 };
17 
18 let getDaysOfMonth = function (date) {
19     var month = date.getMonth() + 1; //注意此处月份要加1
20     var year = date.getFullYear();
21     return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][parseInt(month) - 1];
22 }
23 
24 let getBeginDayOfMouth = function (date) {
25     var month = date.getMonth();
26     var year = date.getFullYear();
27     var d = getDate(year, month, 1);
28     return d.getDay();
29 }
30 
31 let getDisplayInfo = function(date) {
32     if (!isDate(date)) {
33       date = getDate(date)
34     }
35     var year = date.getFullYear();
36 
37     var month = date.getMonth();
38     var d = getDate(year, month);
39 
40     //这个月一共多少天
41     var days = getDaysOfMonth(d);
42 
43     //这个月是星期几开始的
44     var beginWeek = getBeginDayOfMouth(d);
45 
46     return {
47       year: year,
48       month: month,
49       days: days,
50       beginWeek: beginWeek
51     }
52   }
53 
54   let isOverdue = function isOverdue(year, month, day) {
55     let date = new Date(year, month, day);
56     let now = new Date();
57     now = new Date(now.getFullYear(), now.getMonth(), now.getDate());
58     return date.getTime() < now.getTime();
59   }
60   
61   let isToday = function isToday(year, month, day, selectedDate) {
62     let date = new Date(year, month, day);
63     return date.getTime() == selectedDate;
64   }
65 
66 let dateUtils = {
67     isLeapYear,
68     getDaysOfMonth,
69     getBeginDayOfMouth,
70     getDisplayInfo,
71     isOverdue,
72     isToday
73 };
74 
75 export default dateUtils;
utils.js
 1 import React from \'react\';
 2 import dateUtils from \'../../common/utils\'
 3 import CalendarMonth from \'./month\'
 4 
 5 
 6 export default class Calendar extends React.Component {
 7     render() {
 8         let year = this.props.year;
 9         let month = this.props.month;
10         let weekDayArr = [\'日\', \'一\', \'二\', \'三\', \'四\', \'五\', \'六\'];
11         //获取当前日期数据
12         let displayInfo = dateUtils.getDisplayInfo(new Date(year, month, 0));
13         return (
14             <ul className="cm-calendar ">
15                 <ul className="cm-calendar-hd">
16                     {
17                         weekDayArr.map((data, index) => {
18                             return <li key={index} className="cm-item--disabled">{data}</li>
19                         })
20                     }
21                 </ul>
22                 <CalendarMonth year={year} month={month}/>
23             </ul>
24         )
25     }
26 }
calendar.js
 1 import React from \'react\';
 2 import dateUtils from \'../../common/utils\'
 3 import CalendarDay from \'./day\'
 4 
 5 export default class CalendarMonth extends React.Component {
 6 
 7     //获取首次空格
 8     _renderBeginDayOfMouth(beforeDays) {
 9         let html = [];
10         for (let i = 0; i < beforeDays; i++) {
11             html.push(<li key={i} className="cm-item--disabled"></li>);
12         }
13         return html;
14     }
15 
16     //和_renderBeginDayOfMouth类似可以重构掉
17     _renderDays(year, month, days) {
18         let html = [];
19         for(let i = 0; i < days; i++) {
20             html.push(    
21                 <CalendarDay key={i} year={year} month={month} day={i} />
22             )
23         }
24         return html;
25     }
26 
27     render() {
28         let year = this.props.year;
29         let month = this.props.month;
30         let displayInfo = dateUtils.getDisplayInfo(new Date(year, parseInt(month) - 1), 1);
31 console.log(displayInfo)
32         return (
33             <ul className="cm-calendar-bd ">
34                 <h3 className="cm-month calendar-cm-month js_month">{year + \'-\' + month}</h3>
35                 
36                 <ul className="cm-day-list">
37                     { this._renderBeginDayOfMouth( displayInfo.beginWeek) }
38                     { this._renderDays(year, month, displayInfo.days) }
39                 </ul>
40             </ul>
41         )
42     }
43 }
month.js
 1 import React from \'react\';
 2 import dateUtils from \'../../common/utils\'
 3 
 4 export default class CalendarDay extends React.Component {
 5 
 6 
 7     render() {
 8         let year = this.props.year;
 9         let month = this.props.month;
10         let day = this.props.day;
11 
12         let klass = dateUtils.isOverdue(year, parseInt(month) - 1, day) ? \'cm-item--disabled\' : \'\';
13 
14         return (
15             <li year={year} month={month} day={day}  >
16                 <div className="cm-field-wrapper ">
17                     <div className="cm-field-title">{day + 1}</div>
18                 </div>
19             </li>
20         )
21     }
22 }
day.js

这段代码的效果是:

基础框架结构出来后,我们就需要一点一点向上面加肉了,首先我们加一个选中日期,需要一点特效,这里稍微改下代码,具体各位去GitHub上面看代码了,这段代码就不贴出来了,因为我们这里

以上是关于当代前端应该怎么写这个hello world?的主要内容,如果未能解决你的问题,请参考以下文章

怎么用maven写出hello world

为什么程序员写的第一个程序是“Hello World!”

为什么程序员写的第一个程序是“Hello World!”

为什么程序员写的第一个程序是“Hello World!”

怎样写一个Hello World!

牛!Hello World 发明人 80 岁还在写代码