diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..e401845
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,4 @@
+types
+node_modules/
+dist/
+assets/
\ No newline at end of file
diff --git a/.eslintrc b/.eslintrc
index b85ad48..63e374d 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,171 +1,8 @@
 {
-  "extends": "eslint:recommended",
-  "rules": {
-    // 声明
-    "no-use-before-define": "error",
-    //禁止定义前使用
-
-    // 对象
-    "no-dupe-keys": "error",
-    // 禁止在对象字面量中出现重复的键
-    "quote-props": [
-      "error",
-      "as-needed"
-    ],
-    // 对象属性只在需要的时候加引号
-
-    // 字符串
-    "quotes": [
-      "error",
-      "double",
-      {
-        "allowTemplateLiterals": true
-      }
-    ],
-    // 字符串开头和结束使用双引号
-    "no-useless-concat": "error",
-    // 禁止没有必要的字符拼接
-    "no-useless-escape": "error",
-    // 禁用不必要的转义
-
-    // 函数
-    "no-dupe-args": "error",
-    // 禁止在 function 定义中出现重复的参数
-    "space-before-function-paren": "error",
-    // 函数括号前必须要有空格
-
-    // 变量
-    "no-undef": "error",
-    // 禁止使用未声明的变量
-
-    // 比较运算符 & 相等运算符
-    "eqeqeq": "error",
-    // 使用 === 和 !== 代替 == 和 !=
-    "no-unneeded-ternary": "error",
-    //禁止可以在有更简单的可替代的表达式时使用三元操作符
-
-    // 条件
-    "default-case": "error",
-    // 要求 Switch 语句中有 Default 分支
-    "no-else-return": "error",
-    // 如果 if 块中包含了一个 return 语句,else 块就成了多余的了。可以将其内容移至块外
-
-    // 代码块
-    "brace-style": [
-      "error",
-      "1tbs",
-      {
-        "allowSingleLine": true
-      }
-    ],
-    // 代码块左括号紧跟上一行结束
-    "curly": [
-      "error",
-      "multi-line"
-    ],
-    // if、else if、else、for、while强制使用大括号,但允许在单行中省略大括号
-
-    // 注释
-    "spaced-comment": "error",
-    // 注释前有空格
-
-    // 空白
-    "indent": [
-      "error",
-      4,
-      {
-        "SwitchCase": 1
-      }
-    ],
-    // 缩进控制4空格
-    "no-mixed-spaces-and-tabs": "error",
-    // 禁止使用 空格 和 tab 混合缩进
-    "space-before-blocks": [
-      "error",
-      "always"
-    ],
-    // 语句块之前的需要有空格
-    "space-infix-ops": [
-      "error",
-      {
-        "int32Hint": false
-      }
-    ],
-    // 要求中缀操作符周围有空格,设置 int32Hint 选项为 true (默认 false) 允许 a|0 不带空格
-    "no-trailing-spaces": [
-      "error",
-      {
-        "skipBlankLines": true
-      }
-    ],
-    // 禁用行尾空格
-    "key-spacing": [
-      "error",
-      {
-        "afterColon": true
-      }
-    ],
-    // 要求在对象字面量的冒号和值之间存在至少有一个空格
-
-
-    // 逗号
-    "comma-style": "error",
-    // 逗号必须放在行末
-    "comma-dangle": [
-      "error",
-      "never"
-    ],
-    // 多行对象字面量中要求不要拖尾逗号
-    "comma-spacing": [
-      "error",
-      {
-        "before": false,
-        "after": true
-      }
-    ],
-    //在变量声明、数组字面量、对象字面量、函数参数 和 序列中禁止在逗号前使用空格,要求在逗号后使用一个或多个空格
-
-
-    // 分号
-    "semi": "error",
-    //不得省略语句结束的分号
-    "semi-spacing": [
-      "error",
-      {
-        "before": false,
-        "after": true
-      }
-    ],
-    //禁止分号周围的空格
-    "no-extra-semi": "error",
-    // 禁用不必要的分号
-
-
-    // 类型转换
-    "no-extra-boolean-cast": "error",
-    // 禁止不必要的布尔类型转换
-
-
-    // 其他最佳实践或规范
-    "no-unexpected-multiline": "error",
-    // 禁止使用令人困惑的多行表达式
-    "no-unreachable": "error",
-    // 禁止在 return、throw、continue 和 break 语句后出现不可达代码
-    "valid-typeof": "error",
-    // 强制 typeof 表达式与有效的字符串进行比较
-    "no-new-wrappers": "error"
-    // 禁止通过 new 操作符使用 String、Number 和 Boolean
-  },
-  "globals": {
-    "window": true,
-    "BI": true,
-    "BICst": true,
-    "Data": true,
-    "Fix": true,
-    "module": true,
-    "describe": true,
-    "it": true,
-    "expect": true,
-    "Dec": true
+  "extends": "plugin:@fui/typescript",
+  "parserOptions": {
+    "ecmaFeatures": {
+      "legacyDecorators": true
+    }
   }
 }
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1efa1e2..9719d54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,4 +48,5 @@ crashlytics-build.properties
 
 /node_modules/
 package-lock.json
-yarn.lock
\ No newline at end of file
+yarn.lock
+/dist
\ No newline at end of file
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..21e56c3
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+@fui:registry=https://npm.fineres.com/
\ No newline at end of file
diff --git a/Gruntfile.js b/Gruntfile.js
deleted file mode 100644
index 70aa6fa..0000000
--- a/Gruntfile.js
+++ /dev/null
@@ -1,121 +0,0 @@
-module.exports = function (grunt) {
-    // Project configuration.
-    grunt.initConfig({
-        pkg: grunt.file.readJSON("package.json"),
-        concat: {
-            options: {
-                separator: ""
-            },
-            bundleJs: {
-                src: [
-                    "src/modules/**/*.js",
-                    "src/index.js"
-                ],
-                dest: "dist/bundle.js"
-            },
-            bundleCss: {
-                src: ["src/css/**/*.css"],
-                dest: "dist/bundle.css"
-            }
-        },
-
-        less: {
-            main: {
-                expand: true,
-                cwd: "src/modules",
-                src: ["**/*.less"],
-                dest: "src/css/",
-                ext: ".css"
-            },
-            dev: {
-                options: {
-                    compress: true,
-                    yuicompress: false
-                }
-            }
-        },
-
-        uglify: {
-            options: {
-                banner:
-                    "/*! <%= pkg.name %> <%= grunt.template.today(\"dd-mm-yyyy\") %> */\n"
-            },
-            dist: {
-                files: {
-                    "dist/bundle.min.js": ["<%= concat.bundleJs.dest %>"]
-                }
-            }
-        },
-
-        cssmin: {
-            bundleCss: {
-                src: "<%= concat.bundleCss.dest %>",
-
-                dest: "dist/bundle.min.css"
-            }
-        },
-
-        jshint: {
-            files: ["src/**/*.js"],
-            options: {
-                globals: {
-                    $: true,
-                    jQuery: true,
-                    console: true,
-                    module: true,
-                    document: true
-                },
-                browser: true,
-                expr: true
-            }
-        },
-        watch: {
-            scripts: {
-                files: ["src/**/*.js", "src/**/*.less"],
-                tasks: ["less", "concat"],
-                options: {
-                    spanw: true,
-                    interrupt: true
-                }
-            },
-            livereload: {
-                options: {
-                    livereload: "<%= connect.options.livereload %>"
-                },
-                files: ["src/**/*.js", "src/**/*.less"]
-            }
-        },
-        connect: {
-            options: {
-                port: 9000,
-                open: true,
-                livereload: 35799,
-                // Change this to '0.0.0.0' to access the server from outside
-                hostname: "localhost"
-            },
-            server: {
-                options: {
-                    port: 9009,
-                    base: "./"
-                }
-            }
-        }
-    });
-
-    grunt.loadNpmTasks("grunt-contrib-uglify");
-    grunt.loadNpmTasks("grunt-contrib-jshint");
-    grunt.loadNpmTasks("grunt-contrib-less");
-    grunt.loadNpmTasks("grunt-contrib-watch");
-    grunt.loadNpmTasks("grunt-contrib-concat");
-    grunt.loadNpmTasks("grunt-contrib-cssmin");
-    grunt.loadNpmTasks("grunt-contrib-connect");
-
-    grunt.registerTask("default", [
-        "jshint",
-        "less",
-        "concat",
-        "connect",
-        "watch"
-    ]);
-    grunt.registerTask("min", ["less", "concat", "uglify", "cssmin"]);
-};
diff --git a/README.md b/README.md
index 78002c7..dde098a 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,14 @@
 ## 安装运行
 `git clone --recursive https://git.fanruan.com/dailer/FineUI-Start.git`
 
-`npm install`
+## 安装依赖
+`yarn install`
 
-`npm run grunt`
+## 打开dev模式
+`yarn start`
+
+## 打包
+`yarn build`
 
 ## 示例效果
 ![](./screenshorts/todolist.gif)
\ No newline at end of file
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..6cf5fff
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1 @@
+module.exports = require('@fui/babel-preset-fineui').configs.ie8;
diff --git a/package.json b/package.json
index a65ed87..ae7946f 100644
--- a/package.json
+++ b/package.json
@@ -7,27 +7,40 @@
     "fineui": "^2.0.0"
   },
   "devDependencies": {
-    "chai": "4.1.2",
-    "eslint": "4.12.0",
-    "eslint-plugin-prettier": "2.3.1",
-    "express": "4.15.2",
-    "grunt": "1.0.1",
-    "grunt-contrib-concat": "1.0.1",
-    "grunt-contrib-connect": "1.0.2",
-    "grunt-contrib-cssmin": "1.0.1",
-    "grunt-contrib-jshint": "1.0.0",
-    "grunt-contrib-less": "1.4.1",
-    "grunt-contrib-uglify": "1.0.1",
-    "grunt-contrib-watch": "1.0.0",
-    "lodash": "4.17.10",
-    "mocha": "4.0.1",
-    "open": "0.0.5",
-    "prettier": "1.8.2"
+    "@fui/babel-preset-fineui": "^1.0.0",
+    "@fui/typescript-configs": "^1.0.1",
+    "@fui/eslint-plugin": "^1.0.7",
+    "@types/jest": "24.0.11",
+    "typescript": "3.9.2",
+    "fork-ts-checker-webpack-plugin": "1.4.3",
+    "autoprefixer": "9.6.1",
+    "babel-loader": "8.0.6",
+    "cross-env": "6.0.0",
+    "css-loader": "3.0.0",
+    "html-webpack-plugin": "3.2.0",
+    "jest": "24.7.1",
+    "jest-css-modules-transform": "2.5.0",
+    "jest-environment-jsdom": "24.7.1",
+    "jest-snapshot": "23.6.0",
+    "less": "^3.11.3",
+    "less-loader": "5.0.0",
+    "mini-css-extract-plugin": "0.7.0",
+    "npm-run-all": "4.1.5",
+    "optimize-css-assets-webpack-plugin": "5.0.3",
+    "postcss-loader": "3.0.0",
+    "postcss-simple-vars": "5.0.2",
+    "source-map-loader": "0.2.4",
+    "style-loader": "0.23.1",
+    "uglifyjs-webpack-plugin": "2.2.0",
+    "webpack": "4.35.2",
+    "webpack-cli": "3.3.5",
+    "webpack-dev-server": "3.7.2",
+    "webpack-merge": "4.2.1"
   },
   "scripts": {
-    "grunt": "grunt",
-    "uglify": "grunt min",
-    "eslint": "eslint src --fix"
+    "start": "webpack-dev-server -p --progress --host 127.0.0.1 --config=webpack/webpack.dev.js --mode development",
+    "build": "webpack -p --progress --config=webpack/webpack.prod.js --mode production",
+    "test": "jest"
   },
   "author": "",
   "license": "MIT"
diff --git a/src/core/javascript/decorator.js b/src/core/javascript/decorator.js
new file mode 100644
index 0000000..e5f32dd
--- /dev/null
+++ b/src/core/javascript/decorator.js
@@ -0,0 +1 @@
+export const { shortcut, Model, model, store } = BI.Decorators;
diff --git a/src/core/typescript/decorator.ts b/src/core/typescript/decorator.ts
new file mode 100644
index 0000000..ee94eb1
--- /dev/null
+++ b/src/core/typescript/decorator.ts
@@ -0,0 +1,3 @@
+export const { shortcut, Model, model, store } = BI.Decorators;
+
+export type Constructor<T> = new(...args: any[]) => T;
diff --git a/src/css/header/header.css b/src/css/header/header.css
deleted file mode 100644
index 051de4b..0000000
--- a/src/css/header/header.css
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
-  列表项的less,其中用到了部分FineUI提供的字号,颜色常量,还有border-radius,box-shadow方法等.请选择性使用.不强制要求
- */
-.my-todolist-header {
-  background-color: #3d4d66;
-}
-.my-todolist-header .my-todolist-title {
-  font-size: 22px;
-  color: #FFF;
-}
-.my-todolist-header .my-todolist-header-editor {
-  background-color: #FFF;
-  -webkit-border-radius: 5px;
-  -moz-border-radius: 5px;
-  border-radius: 5px;
-}
diff --git a/src/css/index.css b/src/css/index.css
deleted file mode 100644
index e69de29..0000000
diff --git a/src/css/list/list.css b/src/css/list/list.css
deleted file mode 100644
index 7635093..0000000
--- a/src/css/list/list.css
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
-  列表项的less,其中用到了部分FineUI提供的字号,颜色常量,还有border-radius方法等.请选择性使用.不强制要求
- */
-.my-todolist-list .my-todolist-list-text {
-  font-size: 16px;
-  font-weight: bold;
-}
-.my-todolist-list .my-todolist-list-count-container {
-  -webkit-border-radius: 10px;
-  -moz-border-radius: 10px;
-  border-radius: 10px;
-  background-color: #3d4d66;
-  color: #ffffff;
-}
diff --git a/src/css/main.css b/src/css/main.css
deleted file mode 100644
index 214161e..0000000
--- a/src/css/main.css
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
-  列表项的less,其中用到了部分FineUI提供的字号,颜色常量,还有border-radius,box-shadow方法等.请选择性使用.不强制要求
- */
-.my-todolist-background {
-  background-color: #f7f8fa;
-}
diff --git a/src/css/nav/nav.css b/src/css/nav/nav.css
deleted file mode 100644
index e69de29..0000000
diff --git a/src/index.js b/src/index.js
index d906090..3a8714d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,7 @@
-!(function () {
-    // 将todolist组件挂载到#wrapper上.
-    BI.createWidget({
-        type: "my.todolist",
-        element: "#wrapper"
-    });
-})();
+import { ToDoList } from "./modules/main";
+
+// 将todolist组件挂载到#wrapper上.
+BI.createWidget({
+    type: ToDoList.xtype,
+    element: "#wrapper"
+});
diff --git a/src/index.less b/src/index.less
index abfac0b..09f8de8 100644
--- a/src/index.less
+++ b/src/index.less
@@ -1,4 +1,4 @@
-@import "../fineui/src/less/index";
+@import "../node_modules/fineui/src/less/index";
 
 @fontUrl: 'font/'; //字体存放路径
 @imageUrl: 'images/1x/'; //图片的基本地址
diff --git a/src/modules/header/header.js b/src/modules/header/header.js
index c7ce6e1..6f490ff 100644
--- a/src/modules/header/header.js
+++ b/src/modules/header/header.js
@@ -1,70 +1,71 @@
-!(function() {
-    /**
-     * 顶部组件,提供输入框添加todo项目
-     * 布局: bi.horizontal_auto 实现水平居中. bi.left_right_vertical_adapt 实现标题是输入框的靠左靠右垂直居中
-     */
-    var ToDoListHeader = BI.inherit(BI.Widget, {
+import { shortcut } from "../../core/javascript/decorator";
+import "./header.less";
 
-        props: {
-            //  指定组件的className
-            baseCls: "my-todolist-header"
-        },
+/**
+ * 顶部组件,提供输入框添加todo项目
+ * 布局: bi.horizontal_auto 实现水平居中. bi.left_right_vertical_adapt 实现标题是输入框的靠左靠右垂直居中
+ */
+@shortcut()
+export class ToDoListHeader extends BI.Widget {
+    static xtype = "my.todolist.header";
 
-        render: function() {
-            var self = this, o = this.options;
-            return {
-                type: "bi.horizontal_auto",  // 水平居中布局
-                items: [
-                    {
-                        el: {
-                            type: "bi.left_right_vertical_adapt",  //  左右垂直居中布局
-                            width: 600,
-                            height: o.height,
-                            items: {
-                                left: [
-                                    {
-                                        el: {
-                                            type: "bi.label",
-                                            cls: "my-todolist-title",
-                                            text: "FineUI ToDoList",
-                                            height: o.height
-                                        }
-                                    }
-                                ],
-                                right: [
-                                    {
-                                        el: {
-                                            type: "bi.editor",
-                                            ref: function(_ref) {
-                                                self.editor = _ref;
-                                            },
-                                            allowBlank: true,
-                                            cls: "my-todolist-header-editor",
-                                            watermark: "添加ToDo",
-                                            width: 300,
-                                            height: 24,
-                                            listeners: [
-                                                {  //  监听bi.editor 组件的"EVENT_ENTER"事件(即敲回车),触发事件ToDoListHeader.EVENT_ADD事件并将输入框值置空
-                                                    eventName: "EVENT_ENTER",
-                                                    action: function() {
-                                                        //  注意:在这里this指向的是bi.editor的实例.通过bi.editor的getValue()方法获取输入框输入值.
-                                                        self.fireEvent(ToDoListHeader.EVENT_ADD, this.getValue());
-                                                        self.editor.setValue("");
-                                                    }
-                                                }
-                                            ]
-                                        }
-                                    }
-                                ]
-                            }
-                        }
-                    }
-                ]
-            };
-        }
-    });
+    static EVENT_ADD = "EVENT_ADD";
+
+    props = {
+        // 指定组件的className
+        baseCls: "my-todolist-header",
+    }
 
-    ToDoListHeader.EVENT_ADD = "EVENT_ADD";
+    render() {
+        const { height } = this.options;
 
-    BI.shortcut("my.todolist.header", ToDoListHeader);
-})();
\ No newline at end of file
+        return {
+            // 水平居中布局
+            type: BI.HorizontalAutoLayout.xtype,
+            items: [{
+                el: {
+                    // 左右垂直居中布局
+                    type: BI.LeftRightVerticalAdaptLayout.xtype,
+                    width: 600,
+                    height: 40,
+                    items: {
+                        left: [
+                            {
+                                el: {
+                                    type: BI.Label.xtype,
+                                    cls: "my-todolist-title",
+                                    text: "FineUI ToDoList",
+                                    height,
+                                },
+                            },
+                        ],
+                        right: [
+                            {
+                                el: {
+                                    type: BI.Editor.xtype,
+                                    ref: _ref => {
+                                        this.editor = _ref;
+                                    },
+                                    allowBlank: true,
+                                    cls: "my-todolist-header-editor",
+                                    watermark: "添加ToDo",
+                                    width: 300,
+                                    height: 24,
+                                    listeners: [
+                                        { // 监听bi.editor 组件的"EVENT_ENTER"事件(即敲回车),触发事件ToDoListHeader.EVENT_ADD事件并将输入框值置
+                                            eventName: "EVENT_ENTER",
+                                            action: () => {
+                                                this.fireEvent(ToDoListHeader.EVENT_ADD, this.editor.getValue());
+                                                this.editor.setValue("");
+                                            },
+                                        },
+                                    ],
+                                },
+                            },
+                        ],
+                    },
+                },
+            }],
+        };
+    }
+}
diff --git a/src/modules/list/list.js b/src/modules/list/list.js
deleted file mode 100644
index d4e0d3a..0000000
--- a/src/modules/list/list.js
+++ /dev/null
@@ -1,90 +0,0 @@
-!(function() {
-    /**
-     * todo项列表
-     *
-     */
-    var List = BI.inherit(BI.Widget, {
-
-        props: {
-            //  指定组件的className
-            baseCls: "my-todolist-list",
-            text: "正在进行"
-        },
-
-        render: function() {
-            var self = this, o = this.options;
-            return {
-                type: "bi.vertical",
-                items: [
-                    {
-                        el: {
-                            type: "bi.vertical_adapt",
-                            height: 40,
-                            items: [
-                                {
-                                    type: "bi.label",
-                                    cls: "my-todolist-list-text",
-                                    textAlign: "left",
-                                    text: o.text,
-                                    width: 580
-                                }, {
-                                    type: "bi.center_adapt",
-                                    cls: "my-todolist-list-count-container",
-                                    width: 20,
-                                    height: 20,
-                                    items: [
-                                        {
-                                            el: {
-                                                type: "bi.label",
-                                                ref: function(_ref) {
-                                                    self.count = _ref;
-                                                },
-                                                text: "0"
-                                            }
-                                        }
-                                    ]
-                                }
-                            ]
-                        }
-                    }, {  // 用bi.vertical布局作为列表项的容器.
-                        type: "bi.vertical",
-                        vgap: 10,
-                        ref: function(_ref) {
-                            self.list = _ref;
-                        },
-                        items: this._createItems(o.items)
-                    }
-                ]
-            };
-        },
-
-        _createItems: function(items) {
-            var self = this;
-            return BI.map(items, function(index, item) {
-                return BI.extend(item, {
-                    type: "bi.multi_select_item",  // 节点采用复选节点展示
-                    selected: item.done,  // 已完成的todo项置为选中状态
-                    disabled: item.done,  // 已完成的todo项置为灰化状态
-                    listeners: [
-                        {  // 为每个todo项添加"EVENT_CHANGE"事件监听,触发组件自身"EVENT_CHANGE"事件
-                            eventName: "EVENT_CHANGE",
-                            action: function(v) {
-                                self.fireEvent("EVENT_CHANGE", v);
-                            }
-                        }
-                    ]
-                });
-            });
-        },
-
-        _setCount: function(count) {
-            this.count.setText(count);
-        },
-
-        populate: function(items) {
-            this.list.populate(this._createItems(items));
-            this._setCount(items.length);
-        }
-    });
-    BI.shortcut("my.todolist.list", List);
-})();
\ No newline at end of file
diff --git a/src/modules/list/list.ts b/src/modules/list/list.ts
new file mode 100644
index 0000000..7a9b1f2
--- /dev/null
+++ b/src/modules/list/list.ts
@@ -0,0 +1,101 @@
+import { Label, VerticalLayout } from 'fineui';
+import { shortcut } from "../../core/typescript/decorator";
+import "./list.less";
+
+/**
+ * todo项列表
+ */
+@shortcut()
+export class List extends BI.Widget {
+    public static xtype = "my.todolist.list";
+
+    private count: Label;
+    private list: VerticalLayout;
+
+    public props = {
+        // 指定组件的className
+        baseCls: "my-todolist-list",
+        text: "正在进行",
+        items: [],
+    }
+
+    public render() {
+        const { text, items } = this.options;
+
+        return {
+            type: BI.VerticalLayout.xtype,
+            items: [
+                {
+                    el: {
+                        type: BI.VerticalAdaptLayout.xtype,
+                        height: 40,
+                        items: [
+                            {
+                                type: BI.Label.xtype,
+                                cls: "my-todolist-list-text",
+                                textAlign: "left",
+                                text,
+                                width: 580,
+                            }, {
+                                type: BI.CenterAdaptLayout.xtype,
+                                cls: "my-todolist-list-count-container",
+                                width: 20,
+                                height: 20,
+                                items: [
+                                    {
+                                        el: {
+                                            type: BI.Label.xtype,
+                                            ref: (_ref: Label) => {
+                                                this.count = _ref;
+                                            },
+                                            text: 0,
+                                        },
+                                    },
+                                ],
+                            },
+                        ],
+                    },
+                }, {
+                    // 用bi.vertical布局作为列表项的容器.
+                    type: BI.VerticalLayout.xtype,
+                    vgap: 10,
+                    ref: (_ref: VerticalLayout) => {
+                        this.list = _ref;
+                    },
+                    items: this.createItems(items),
+                },
+            ],
+        };
+    }
+
+    private createItems(items: Item[]) {
+        return items.map(item => {
+            return BI.extend(item, {
+                type: BI.MultiSelectItem.xtype, // 节点采用复选节点展示
+                selected: item.done, // 已完成的todo项置为选中状态
+                disabled: item.done, // 已完成的todo项置为灰化状态
+                listeners: [
+                    { // 为每个todo项添加"EVENT_CHANGE"事件监听,触发组件自身"EVENT_CHANGE"事
+                        eventName: "EVENT_CHANGE",
+                        action: (v: any) => {
+                            this.fireEvent("EVENT_CHANGE", v);
+                        },
+                    },
+                ],
+            });
+        })
+    }
+
+    private setCount(count: number) {
+        this.count.setText(`${count}`);
+    }
+
+    public populate(items: []) {
+        this.list.populate(this.createItems(items));
+        this.setCount(items.length);
+    }
+}
+
+interface Item {
+    done: boolean,
+}
diff --git a/src/modules/main.js b/src/modules/main.js
index 9152cee..e624d9a 100644
--- a/src/modules/main.js
+++ b/src/modules/main.js
@@ -1,121 +1,119 @@
-!(function() {
-    /**
-     * todolist 组件
-     */
-    var ToDoList = BI.inherit(BI.Widget, {
+import { shortcut } from "../core/javascript/decorator";
+import { ToDoListHeader } from "./header/header";
+import { List } from "./list/list";
+import "./main.less";
 
-        props: {
-            baseCls: "fine-to-do-list"
-        },
+/**
+ * todolist 组件
+ */
+@shortcut()
+export class ToDoList extends BI.Widget {
+    static xtype = "my.todolist";
 
-        // 生命周期函数,在组件创建前
-        beforeCreate: function() {
-            //  初始化存储数据
-            this.list = localStorage.getItem("fine-todolist") ? JSON.parse(localStorage.getItem("fine-todolist")) : [];
-        },
+    props = {
+        baseCls: "fine-to-do-list",
+    }
 
-        render: function() {
-            var self = this, o = this.options;
-            return {
-                type: "bi.vtape",  // vtape布局,顶部高度固定,下部分列表占满高度
-                items: [
-                    {
-                        el: {
-                            type: "my.todolist.header",  // 顶部组件
-                            listeners: [
-                                {  // 监听组件的EVENT_ADD事件,新建todo项
-                                    eventName: "EVENT_ADD",
-                                    action: function(v) {
-                                        self.addToDo(v);
-                                    }
-                                }
-                            ],
-                            height: 40
-                        },
-                        height: 40
-                    }, {
-                        type: "bi.horizontal_auto",  // 水平居中布局
-                        cls: "my-todolist-background",  // 添加className
-                        items: [
-                            {
-                                el: {
-                                    type: "my.todolist.list",  // need todo项列表
-                                    ref: function(_ref) {
-                                        self.todolist = _ref;
-                                    },
-                                    items: this._getNeedTodoList(),
-                                    text: "正在进行",
-                                    listeners: [
-                                        {  // 监听EVENT_CHANGE事件,完成某一项todo
-                                            eventName: "EVENT_CHANGE",
-                                            action: function(v) {
-                                                self.finishTodo(v);
-                                            }
-                                        }
-                                    ],
-                                    width: 600
-                                }
-                            }, {
-                                el: {
-                                    type: "my.todolist.list",  //  已经完成的todo项列表
-                                    text: "已经完成",
-                                    items: this._getAlreadyDoneList(),
-                                    ref: function(_ref) {
-                                        self.donelist = _ref;
+    // 生命周期函数,在组件创建前
+    beforeCreate() {
+        // 初始化存储数据
+        this.list = localStorage.getItem("fine-todolist") ? JSON.parse(localStorage.getItem("fine-todolist")) : [];
+    }
+
+    render() {
+        return {
+            type: BI.VTapeLayout.xtype, // vtape布局,顶部高度固定,下部分列表占满高度
+            items: [
+                {
+                    el: {
+                        type: ToDoListHeader.xtype, // 顶部组件
+                        listeners: [
+                            { // 监听组件的EVENT_ADD事件,新建todo项
+                                eventName: "EVENT_ADD",
+                                action: v => {
+                                    this.addToDo(v);
+                                },
+                            },
+                        ],
+                        height: 40,
+                    },
+                    height: 40,
+                }, {
+                    type: BI.HorizontalAutoLayout.xtype, // 水平居中布局
+                    cls: "my-todolist-background", // 添加className
+                    items: [
+                        {
+                            el: {
+                                type: List.xtype, // need todo项列表
+                                ref: _ref => {
+                                    this.todolist = _ref;
+                                },
+                                items: this._getNeedTodoList(),
+                                text: "正在进行",
+                                listeners: [
+                                    { // 监听EVENT_CHANGE事件,完成某一项todo
+                                        eventName: "EVENT_CHANGE",
+                                        action: v => {
+                                            this.finishTodo(v);
+                                        },
                                     },
-                                    width: 600
-                                }
-                            }
-                        ]
-                    }
-                ]
-            };
-        },
+                                ],
+                                width: 600,
+                            },
+                        }, {
+                            el: {
+                                type: List.xtype, // 已经完成的todo项列表
+                                text: "已经完成",
+                                items: this._getAlreadyDoneList(),
+                                ref: _ref => {
+                                    this.donelist = _ref;
+                                },
+                                width: 600,
+                            },
+                        },
+                    ],
+                }],
+        };
+    }
 
-        _updateLocalStorage: function() {
-            localStorage.setItem("fine-todolist", JSON.stringify(this.list));
-        },
+    _updateLocalStorage() {
+        localStorage.setItem("fine-todolist", JSON.stringify(this.list));
+    }
 
-        _getNeedTodoList: function() {
-            return BI.filter(this.list, function(index, item) {
-                return !item.done;
-            });
-        },
+    _getNeedTodoList() {
+        return BI.filter(this.list, (index, item) => !item.done);
+    }
 
-        _getAlreadyDoneList: function() {
-            return BI.filter(this.list, function(index, item) {
-                return item.done;
-            });
-        },
+    _getAlreadyDoneList() {
+        return BI.filter(this.list, (index, item) => item.done);
+    }
 
-        /**
-         * 添加todo项
-         * @param text todo项的内容
-         */
-        addToDo: function(text) {
-            this.list.push({
-                value: BI.UUID(),
-                text: text,
-                done: false
-            });
-            this.todolist.populate(this._getNeedTodoList());
-            this._updateLocalStorage();
-        },
+    /**
+     * 添加todo项
+     * @param text todo项的内容
+     */
+    addToDo(text) {
+        this.list.push({
+            value: BI.UUID(),
+            text,
+            done: false,
+        });
+        this.todolist.populate(this._getNeedTodoList());
+        this._updateLocalStorage();
+    }
 
-        /**
-         * 完成某一项todo
-         * @param v todo项的value
-         */
-        finishTodo: function(v) {
-            BI.some(this.list, function(index, item) {
-                if (item.value === v) {
-                    item.done = true;
-                }
-            });
-            this.todolist.populate(this._getNeedTodoList());
-            this.donelist.populate(this._getAlreadyDoneList());
-            this._updateLocalStorage();
-        }
-    });
-    BI.shortcut("my.todolist", ToDoList);
-})();
+    /**
+     * 完成某一项todo
+     * @param v todo项的value
+     */
+    finishTodo(v) {
+        BI.some(this.list, (index, item) => {
+            if (item.value === v) {
+                item.done = true;
+            }
+        });
+        this.todolist.populate(this._getNeedTodoList());
+        this.donelist.populate(this._getAlreadyDoneList());
+        this._updateLocalStorage();
+    }
+}
diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts
new file mode 100644
index 0000000..6a28a08
--- /dev/null
+++ b/src/types/globals.d.ts
@@ -0,0 +1,10 @@
+interface Obj {
+    [key: string]: any;
+}
+
+declare let BI: Obj & import('fineui').BI;
+declare const Fix: Obj;
+
+declare interface String {
+    replaceAll(regx: string | RegExp, callback: (str: string) => void): string;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9459ffe
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,7 @@
+{
+    "extends": "@fui/typescript-configs/application.json",
+    "compilerOptions": {
+        "strict": false,
+        "allowJs": true,
+    },
+}
diff --git a/webpack/dirs.js b/webpack/dirs.js
new file mode 100644
index 0000000..09fc4e7
--- /dev/null
+++ b/webpack/dirs.js
@@ -0,0 +1,9 @@
+const path = require("path");
+module.exports = {
+    DEST: path.resolve(__dirname, "../dist"),
+    NODE_MODULES: path.resolve(__dirname, "../node_modules"),
+    PRIVATE: path.resolve(__dirname, "../private"),
+    BABEL_CONFIG: path.resolve(__dirname, "../babel.config.js"),
+    IE8_BABEL_CONFIG: path.resolve(__dirname, "../babel.config.ie8.js"),
+    SRC: path.resolve(__dirname, "../src"),
+};
diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js
new file mode 100644
index 0000000..fa765f5
--- /dev/null
+++ b/webpack/webpack.common.js
@@ -0,0 +1,63 @@
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const autoprefixer = require("autoprefixer");
+
+const dirs = require("./dirs");
+
+module.exports = {
+    entry: {
+        bundle: [
+            "./src/index.js",
+        ],
+    },
+    resolve: {
+        mainFields: ["module", "main"],
+        extensions: [".js", ".ts"],
+    },
+    stats: {
+        children: false,
+        modules: false,
+    },
+    module: {
+        rules: [
+            {
+                test: /\.(js|ts)$/,
+                include: [dirs.NODE_MODULES, dirs.SRC],
+                use: [{
+                    loader: "babel-loader",
+                    options: {
+                        configFile: dirs.BABEL_CONFIG,
+                    },
+                }, {
+                    loader: "source-map-loader",
+                    options: {
+                        enforce: "pre",
+                    },
+                }],
+            },
+            {
+                test: /\.(css|less)$/,
+                use: [
+                    MiniCssExtractPlugin.loader,
+                    {
+                        loader: "css-loader",
+                        options: {
+                            url: false,
+                        },
+                    },
+                    {
+                        loader: "postcss-loader",
+                        options: {
+                            plugins: [autoprefixer],
+                        },
+                    },
+                    {
+                        loader: "less-loader",
+                        options: {
+                            relativeUrls: false,
+                        },
+                    },
+                ],
+            },
+        ],
+    },
+};
diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js
new file mode 100644
index 0000000..190be31
--- /dev/null
+++ b/webpack/webpack.dev.js
@@ -0,0 +1,53 @@
+const merge = require("webpack-merge");
+const path = require("path");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
+const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
+
+const dirs = require("./dirs");
+
+const common = require("./webpack.common.js");
+
+module.exports = merge(common, {
+    devtool: "eval-source-map",
+    entry: {
+    },
+    output: {
+        path: dirs.DEST,
+        filename: "[name].[contenthash].js",
+    },
+    devServer: {
+        open: true,
+        contentBase: path.join(__dirname, ".."),
+        port: 9002,
+        liveReload: true,
+    },
+    plugins: [
+        new ForkTsCheckerWebpackPlugin({
+        }),
+        new MiniCssExtractPlugin({
+            path: dirs.DEST,
+            filename: "[contenthash].css",
+        }),
+        new HtmlWebpackPlugin({
+            template: path.resolve(__dirname, "../index.html"),
+            chunks: ["bundle"],
+            chunksSortMode: "manual",
+            nodeModules: path.resolve(__dirname, "../node_modules"),
+        }),
+        new OptimizeCssAssetsPlugin({
+            assetNameRegExp: /\.css$/g,
+            cssProcessor: require("cssnano"),
+            cssProcessorPluginOptions: {
+                preset: ["default", {
+                    discardComments: {
+                        removeAll: true,
+                    },
+                    normalizeUnicode: false,
+                }],
+            },
+            canPrint: true,
+        }),
+    ],
+});
diff --git a/webpack/webpack.prod.js b/webpack/webpack.prod.js
new file mode 100644
index 0000000..29b363d
--- /dev/null
+++ b/webpack/webpack.prod.js
@@ -0,0 +1,60 @@
+const webpack = require("webpack");
+const merge = require("webpack-merge");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
+const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
+const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
+
+const dirs = require("./dirs");
+
+const common = require("./webpack.common.js");
+
+module.exports = merge.smart(common, {
+    mode: "production",
+    optimization: {
+        minimizer: [
+            new UglifyJsPlugin({
+                parallel: true,
+                sourceMap: true,
+                uglifyOptions: {
+                    ie8: true,
+                    output: {
+                        comments: false,
+                    },
+                },
+            }),
+        ],
+    },
+
+    devtool: "hidden-source-map",
+
+    output: {
+        path: dirs.DEST,
+        filename: "bundle.js",
+    },
+
+    plugins: [
+        new ForkTsCheckerWebpackPlugin({
+        }),
+        new MiniCssExtractPlugin({
+            path: dirs.DEST,
+            filename: "bundle.css",
+        }),
+        new webpack.BannerPlugin({
+            banner: `time: ${new Date().toLocaleString()}`,
+        }),
+        new OptimizeCssAssetsPlugin({
+            assetNameRegExp: /\.css$/g,
+            cssProcessor: require("cssnano"),
+            cssProcessorPluginOptions: {
+                preset: ["default", {
+                    discardComments: {
+                        removeAll: true,
+                    },
+                    normalizeUnicode: false,
+                }],
+            },
+            canPrint: true,
+        }),
+    ],
+});