vlambda博客
学习文章列表

前端框架 san 使用

前端的框架作用其实都是为了让你更快速、更好地完成前端开发,一个需求如果单纯使用 HTML、JS 需要几百行代码实现,而使用框架几行代码即可搞定。另外框架可以很好地做到模块化、组件化开发,以数据来渲染 UI,不需要通过繁琐的 API 来操作 DOM。san 是一个开源的MVVM前端框架,只有 15k 左右,它与 Vue 和 React 类似。

先看一个完整例子:

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>第一个例子</title> <script src="https://unpkg.com/san@latest"></script> <!-- <script ></script> --></head><body> <script> var MyApp = san.defineComponent({ template: `<p>Hello {{name}}!</p>`, initData: function() { return { name: 'lefe' }; } }) var app = new MyApp(); app.attach(document.body);</script></body></html>

上面的代码运行后,结果如下:

上面的例子中,通过数据来渲染 UI,当 name 值改变的时候 UI 将会自动改变。我们一起看看其它使用方式:


san 可以通过引用的方式(直接从线上获取代码)使用 san:

// 开发版本
<script src="https://unpkg.com/san@latest/dist/san.dev.js"></script>
// 线上版本
<script src="https://unpkg.com/san@latest"></script>

也可以通过本地引用来使用 san,通过 sudo npm install -g san 全局安装 san,复制一份到开发目录,这样方便看源码。看一个例子:

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>day1</title>
  // 必须要加入
   <script src="../san-sdk/dist/san.dev.js"></script>
</head>

<body>
   <script>
       var MyApp = san.defineComponent({
           template: '<p>Hello {{name}}!</p>',

           initData: function () {
               return {
                   name: 'San'
              };
          }
      });

       var myApp = new MyApp();
       myApp.attach(document.body);
   </script>
</body>

</html>

通过 san.defineComponent来创建一个组件,函数原型为:

function defineComponent(proto, SuperComponent) {}

创建组件时需要传入一个对象,SuperComponent 是组件的父类,可忽略。

模板

定义组件时需要提供一个模板:

<p>Hello {{name}}!</p>

San使用基于HTML的模板,允许你以声明的方式将渲染的DOM和San实例的数据绑定

看几个例子:

例1:插值

var MyApp = san.defineComponent({
   template: '<p>Hello {{name}}!</p>',

   initData: function () {
       return {
           name: 'San'
      };
  }
});

例2:过滤器

<!-- 过滤器,name  upper 的输入 -->
<script>
   var MyApp = san.defineComponent({
       template: '<p>Hello {{name | upper}}!</p>',

       initData: function () {
           return {
               name: 'san',
          };
      },
       filters: {
           upper: function(value) {
               return value.toUpperCase();
          }
      }
  });
   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

例3:双向绑定

<!-- 双向绑定 -->
<script>
   var MyApp = san.defineComponent({
       template: ''
           + '<div>'
           + '<input value="{= name =}" placeholder="please input">'
           + '<p>Hello {{name}}!</p>'
           + '</div>'
  });

   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

例4:方法调用

<!-- 方法调用 -->
<script>
   var MyApp = san.defineComponent({
       template: '<u>{{sum(a, b)}}</u>',
       sum: function (a, b) {
           return a + b;
      },
       initData: function () {
           return {
               a: 1,
               b: 10,
          }
      }
  })
   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

例5:表达式

<!-- 表达式 -->
<script>
   var MyApp = san.defineComponent({
       template: ''
           + '<div>'
           + '<p>a+b = {{ a+b }}</p>'
           + '<p>LiLei &amp; HanMeiMei are a couple.</p>'
           + '<p>1 + 1 &lt; 3</p>'
           + '<p>a+100 = {{ a+ 100 }}</p>'
           + '</div>',
       initData: function () {
           return {
               a: 1,
               b: 10,
          }
      }
  })
   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

数据

通过 initData 来初始化数据,并通过 this.data来对数据进行处理:

    <script>
       var MyApp = san.defineComponent({
           template: '<p>Hello {{width}}!</p>',
           initData: function () {
               return {
                   width: 200,
                   top: [1,2,3,4,5,6],
                   left: -1000
              };
          },

           attached: function () {
               // 获取值
               console.log(this.data.get('width')); // 200
               // 获取多个值
               let {top, left} = this.data.get();
               console.log("top=" + top + "," + "left=" + left);
               // 设置值
               this.data.set('width', 300);
               // 类似与 map 操作
               this.data.apply('width', function(w) {
                   return w * 2;
              });
               // 数组添加数据
               this.data.push('top', 4);
               // 删除最后一条数据
               this.data.pop('top');
               // 在开头插入一条数据
               this.data.unshift('top', 10);
               // 在开头删除一条数据
               this.data.shift('top');
               // 删除数据
               this.data.remove('top', 3);
               this.data.removeAt('top', 1);
               // 删除 0,2 下标对应的数据
               this.data.splice('top', [0, 2]);
               console.log("top=" + this.data.get('top'));
          }
      });
       var myApp = new MyApp();
       myApp.attach(document.body);
   </script>

条件

<script>
   var MyApp = san.defineComponent({
       template: '<div>'
               + '<p s-if="state === 0"> state = 0 </p>'
               + '<p s-elif="state === 1"> state = 1 </p>'
               + '<p s-else> state = none </p>'
               + '<span s-if="isOnline">Hello!</span>'
               + '<span s-else>Offline</span>'
               + '</div>',
       initData: function () {
           return {
               state: 1,
               isOnline: false,
          };
      },
  });
   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

循环

<script>
   var MyApp = san.defineComponent({
       template: '<div>'
           + '<p s-for="v, index in persons">{{ v }}</p>'
           + '</div>',
       initData: function () {
           return {
               persons: [
                   "lefe",
                   "wsy",
                   "sy"
              ]
          };
      },
  });
   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

事件

事件是开发中最常用的行为管理方式。通过 on- 前缀,可以将事件的处理绑定到组件的方法上。在 San 中,无论是 DOM 事件还是组件的自定义事件,都通过 on- 前缀绑定,没有语法区分。

<script>
   var MyApp = san.defineComponent({
       template: '<div>'
           + '<p s-for="v, index in persons" on-click="clickAction(v)">{{ v }}</p>'
           + '<p on-click="clicker($event)"> custom event </p>'
           + '</div>',
       initData: function () {
           return {
               persons: [
                   "lefe",
                   "wsy",
                   "sy"
              ]
          };
      },
       // 点击事件、携带参数
       clickAction: function(name) {
           console.log("click = " + name);
      },
       // $event 将引用到 DOM Event 对象
       clicker: function(event) {
           console.log(event);
      },
  });
   var myApp = new MyApp();
   myApp.attach(document.body);
</script>

插槽

组件可以通过外部来创建:

<script>
       // 定义一个插槽 slot 组件
       var Pannel = san.defineComponent({
           // 最终渲染顺序按 slot 定义的顺序渲染
           template: '<div>'
               + '<div class="head" on-click="toggle">title</div>'
               + '<p><slot name="bottom"></slot></p>'
               + '<p><slot name="top"></slot></p>'
               + '</div>',

           initData: function () {
               return { name: 'Panel' };
          },

           toggle: function () {
               console.log("toggle");
          },
      });

       var MyApp = san.defineComponent({
           components: {
               'ui-pannel': Pannel
          },

           template: '<div><ui-pannel>'
               + '<h1 slot="top">I am top {{name}}</h1>'
               + '<h4 slot="bottom">I am bottom {{name}}</h4>'
               + '</ui-pannel></div>',

           initData: function () {
               return {
                   name: "lefe"
              };
          },
      });
       var myApp = new MyApp();
       myApp.attach(document.body);
   </script>

组件

组件是 San 的基本单位,是独立的数据、逻辑、视图的封装单元。从页面的角度看,组件是 HTML 元素的扩展。从功能模式的角度看,组件是一个 ViewModel。

<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="../san-sdk/dist/san.dev.js"></script></head>
<body> <script> var Label = san.defineComponent({ template: '<p>hello world</p>', });
var MyApp = san.defineComponent({ template: '<div>' // + '<my_label s-ref="label"></my_label>' + '<ul><li s-for="item in list">{{item}}</li></ul>' + '<p>name : {{name}}</p>' + '</div>', // 使用其它组件必须申明 components: { 'my_label': Label, }, // 计算属性 computed: { name: function () { return this.data.get('fName') + ' ' + this.data.get('sName'); } }, initData: function () { return { fName: "suyan", sName: "wang", } }, compiled: function () { console.log("compiled 组件视图模板编译完成"); }, inited: function () { console.log("inited 组件实例初始化完成"); }, created: function () { console.log("created 组件元素创建完成"); }, attached: function () { console.log("attached 组件已被附加到页面中"); }, detached: function () { console.log("detached 组件从页面中移除"); }, disposed: function () { console.log("disposed 组件卸载完成"); }, }) var myApp = new MyApp({ // 初始化的时候携带数据 data: { list: ['lefe', 'suyan', 'sy', 'wsy', 'lefex'] } }); myApp.attach(document.body);</script></body>
</html>


推荐阅读:



前端