Arduino驅動ILI9341彩屏(一)——顏色問題

 

最近在淘寶的店鋪上淘到了一塊ILI9341的彩色液晶屏,打算研究一下如何使用。

淘寶店鋪購買屏幕之後有附源代碼可供下載,代碼質量慘不忍睹,各種縮進不規範就不說了,先拿來試一下吧。

這是淘寶店鋪代碼的核心部分:

void setup()
{
  Lcd_Init();
 //LCD_Clear(0xf800);
}

void loop()
{  
   LCD_Clear(0xf800);
   LCD_Clear(0x07E0);
   LCD_Clear(0x001F);
  /*   
  for(int i=0;i<1000;i++)
  {
    Rect(random(300),random(300),random(300),random(300),random(65535)); // rectangle at x, y, with, hight, color
  }*/
  
//  LCD_Clear(0xf800);
}

代碼裏面的setup()和loop()是arduino特有的主函數,和普通C程序的main()函數一樣。

setup()函數在開機時只運行一次,運行完之後就開始循環運行loop()函數。

程序先在setup()函數里做了一下初始化操作Lcd_Init(),接着開始連續用不同顏色清屏。

這裏的LCD_Clear()就是清屏函數了,原型如下:

void LCD_Clear(unsigned int j)                   
{    
  unsigned int i,m;
 Address_set(0,0,240,320);
  //Lcd_Write_Com(0x02c); //write_memory_start
  //digitalWrite(LCD_RS,HIGH);
  digitalWrite(LCD_CS,LOW);


  for(i=0;i<240;i++)
    for(m=0;m<320;m++)
    {
      Lcd_Write_Data(j>>8);
      Lcd_Write_Data(j);

    }
  digitalWrite(LCD_CS,HIGH);   
}

縮進不規範就不吐槽了(;へ:),連變量名都起得亂七八糟,簡直慘不忍睹。稍微重寫了一下函數,長這樣:

void LCD_Clear(unsigned int color){
  Address_set(0,0,240,320);
  digitalWrite(LCD_CS,LOW);
  for(int i=0;i<240;i++){
    for(int m=0;m<320;m++){
      Lcd_Write_Data(color>>8);
      Lcd_Write_Data(color);
    }
  }
  digitalWrite(LCD_CS,HIGH);
}

這個函數先使用Address_set()設置了刷新區域,然後把LCD_CS針腳電壓拉低,之後循環寫入color。

color分兩次寫入,一次寫入高八位(16位整形前面8個bit),一次寫入低八位。

看上去好像沒什麼問題,但loop()函數中LCD_Clear()卻是直接用十六進制寫入的。

寫一個RGB()函數把RGB顏色轉換成十六進制,不是更人性化嗎?

讀了一遍源代碼,結果真的找到了店家的RGB函數:

int RGB(int r,int g,int b)
{return r << 16 | g << 8 | b;
}

還是不規範的縮進(╯︵╰)。但有總比沒有好,輸出紅色試一下:

void setup()
{
  Lcd_Init();
  LCD_Clear(RGB(255,0,0));
}

void loop()
{  
   //nothing
}

出故障了。

 

 

 

Arduino重啟后,屏幕輸出了黑色!再試着排除一下故障,把RGB(255,0,0)改成RGB(0,255,0),輸出綠色試試:

 

 

 

 

結果輸出了橙色!

之後我又反覆嘗試了,沒有一次輸出正確的顏色。莫非是這個RGB()函數有問題,淘寶店鋪才用十六進制数字?

再仔細推導了一下:return r << 16 | g << 8 | b;把紅色左移16位,綠色左移8位,藍色不動,所以合成的二進制應該是這樣的:

RRRRRRRRGGGGGGGGBBBBBBBB

R代表紅色位,G代表綠色位,B代表藍色位,每種顏色8位,總共24位。計算了一下可能性:

 

 

 總共1677萬種可能,也就是1677萬種顏色,這就是普通電腦的真彩顏色。但LCD_Clear()函數是這麼寫的:

Lcd_Write_Data(color>>8);
Lcd_Write_Data(color);

總的只能寫入十六個bit,也就是16位,這和24位對不上號啊?

再回頭看了一下,店鋪代碼的setup()函數中有這樣一行白色清屏指令:

//LCD_Clear(0xf800);

0xf800換算成十進制,是63488,有沒有感覺很接近一個數?

沒錯,就是65535,單個16位無符號整數的最大儲存範圍。

16位整型變量,顧名思義就是用16個0和1組成的變量。可以儲存的整數範圍是-32768 ~ 32767,32768 + 32767剛好等於65535,換算到二進制,就是1111111111111111,16個1。

 

這時,真相出現了——這台機器所採用的,是16位顏色,也被成為RGB565顏色模式。

 

早期的16位計算機由於架構的設計,一次只能處理一個16位二進制數。而圖形显示對速度要求特別高,所以一個像素必須要用一個16進制數來表示,也就是16位顏色。

如果用採用24位顏色,就需要兩個16進制數,也就是2Bytes,速度就慢了一半。

而每個像素都是使用紅黃藍三基色來显示的,所以一個16進制數必須分3份,來分別表示紅、黃、藍的數據。

這就出現了一個問題:

16 / 3 = 5.33333

紅黃藍三種顏色平均佔用5.33333個bit。

可bit是計算機存儲的基本單位,要麼是1,要麼是0,不能再分割。必須要有一種顏色多用一個位,才能充分單個利用16進制整數。

人體的綠色視錐細胞比較敏感,正好,那綠色就用6位,紅色藍色就用5位吧。

這就是著名的RGB565模式,總共能存儲65535種顏色。

早期的遊戲都採用這種模式,所以顏色不夠豐富,很有特色:

 

 

 

 這塊ILI9341显示屏模塊(注意不是ILI9341芯片本身)也剛好只有16根數據引腳,所以就採用了這種RGB565的顏色模式。

找到了問題,那就改一下程序吧:

int RGB(int r,int g,int b)
{
  return r << 11 | g << 5 | b;
}

光改RGB()函數還不夠,現在使用了RGB565模式,所以綠色範圍是從0-63,紅色、藍色的範圍是0-31。

所以還得改setup裏面的清屏函數:

void setup()
{
  Lcd_Init();
  LCD_Clear(RGB(0,63,0));
}

重新下載了程序,屏幕成功显示,輸出了正確的綠色!⁄(⁄⁄•⁄ω⁄•⁄⁄)⁄

 

 

 那麼問題來了,開頭店家給的LCD_Clear(0xf800)這條清屏指令,是怎麼來的?畢竟他連RGB565都不知道呢!

這是我提供的一種可能性:

“0xf600試一下?”

“不行,太灰了!”

“那0xf700呢?”

“還是不行!老闆,我們都試了一下午了,肯定是屏幕壞了!”

“加油,還差一點點了,肯定可以的!”

“0xf800好像還行,但是還是有點灰!”

“沒關係,反正買家能點亮屏幕就行,其他的我們不管!”

“……”

所以這家淘寶店鋪根本不知道自己在買什麼。ヽ( ̄▽ ̄)ノ

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

上海新能源汽車展8月23舉行 吉利將攜帝豪PHEV亮相

進入2017年以來,上海市新能源汽車推廣應用和產業發展繼續保持高增長,1-5月新能源汽車推廣上牌數量達到10699輛。據瞭解,目前在上海市場上銷售的新能源車型累計超過100款,其中本地品牌占比為35%,其他省市品牌占比達到65%,市場累計推廣前5位的品牌依次為比亞迪、榮威、北汽、奇瑞、Tesla。

當前,上海已成為新能源汽車全球保有量最大的城市,吉利汽車自然不會放過這個巨大的市場。在最近上海市經信委公佈的4批新能源汽車備案資訊表中,吉利分別在第三批、第四批目錄中均有車型進入。據悉,吉利進入上海市新能源汽車備案資訊表中的車型為吉利帝豪EV300及吉利帝豪PHEV。

吉利帝豪EV300自推出以來,頗受市場的歡迎,今年5月還登頂了新能源汽車單車銷量排行板的第一位,而帝豪PHEV作為吉利旗下首款插電式混合動力車型,自公佈以來也是備受關注。

據瞭解,吉利帝豪PHEV外觀上與吉利帝豪EV基本相同。內飾方面,帝豪PHEV配備三輻式真皮方向盤並集成了CCS定速巡航、多媒體播放、語音控制等控制按鈕。而與帝豪EV不同的是,在中控部分帝豪PHEV增加了旋鈕式檔位,使車輛可在純電動、混合動力等模式中進行切換,增強了操作的便利性及駕駛體驗。

最亮眼的部分是帝豪PHEV採用了被稱為“聯擎”的功率分流式混合動力技術,搭載代號為JLγ-4G15H的1.5L發動機與兩台高性能電機和11.3kWh的三元鋰電池組組成的插電式混合動力系統。新車最高時速可達175km/h,NEDC工況油耗1.5L/100km,HEV模式下綜合工況油耗5.1L/100km,NEDC工況下純電續航里程61km。

作為全國插電式混合動力最大銷售城市,吉利帝豪PHEV本次順利進入上海新能源汽車目錄無疑是個極好的信號。為了與各位大咖搶佔市場份額,吉利汽車將攜帝豪EV300,帝豪PHEV亮相2017上海國際新能源汽車產業博覽會。其中帝豪PHEV是進入上海新能源汽車目錄後,首度亮相上海。

據瞭解,2017上海國際新能源汽車產業博覽會是由充電設施線上網、廣東省充電設施協會、廣東省新能源汽車產業協會、中國土木工程學會城市公共交通學會和振威展覽股份聯合舉辦,展示面積達45000平米,參展企業涵蓋了整車、核心三電(電池、電機、電控)、充電設備等產業板塊,是我國新能源汽車產業領域最專業的展覽展示和技術交流的綜合性展會平臺。

除吉利外,比亞迪、申龍客車、珠海銀隆、上汽集團、上饒客車、中植新能源、中通、江淮、眾泰、知豆、南京金龍、成功汽車、新吉奧集團、瑞馳新能源、福汽新龍馬等新能源汽車企業,以及精進電動、英威騰、東風電機、力神、沃特瑪、國軒高科、地上鐵、特來電、科陸、巴斯巴、萬馬專纜、奧美格、瑞可達等核心三電及零部件知名企業將亮相本次展會。

參觀預登記,請點擊:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

組件設計 —— 重新認識受控與非受控組件

重新定義受控與非受控組件的邊界

對非受控組件與受控組件作了如圖中下劃線的邊界定義。一經推敲, 該定義是缺乏了些完整性嚴謹性的, 比如針對非表單組件(彈框、輪播圖)如何劃分受控與非受控的邊界? 又比如非受控組件是否真的如文案上所說的數據的展示與變更都由 dom 自身接管呢?

在非受控組件中, 通常業務調用方只需傳入一個初始默認值便可使用該組件。以 Input 組件為例:

// 組件提供方
function Input({ defaultValue }) {
  return <input defaultValue={defaultValue} />
}

// 調用方
function Demo() {
  return <Input defaultValue={1} />
}

在受控組件中, 數值的展示與變更則分別由組件的 statesetState 接管。同樣以 Input 組件為例:

// 組件提供方
function Input() {
  const [value, setValue] = React.useState(1)
  return <input value={value} onChange={e => setValue(e.target.value)} />
}

// 調用方
function Demo() {
  return <Input />
}

有意思的一個問題來了, Input 組件到底是受控的還是非受控的? 我們甚至還可以對代碼稍加改動成 <Input defaultValue={1} /> 的最初調用方式:

// 組件提供方
function Input({ defaultValue }) {
  const [value, setValue] = React.useState(defaultValue)
  return <input value={value} onChange={e => setValue(e.target.value)} />
}

// 調用方
function Demo() {
  return <Input defaultValue={1} />
}

儘管此時 Input 組件本身是一個受控組件, 但與之相對的調用方失去了更改 Input 組件值的控制權, 所以對調用方而言, Input 組件是一個非受控組件。值得一提的是, 以非受控組件的使用方式去調用受控組件是一種反模式, 在下文中會分析其中的弊端。

如何做到不管對於組件提供方還是調用方 Input 組件都為受控組件呢? 提供方讓出控制權即可, 調整代碼如下:

// 組件提供方
function Input({ value, onChange }) {
  return <input value={value} onChange={onChange} />
}

// 調用方
function Demo() {
  const [value, setValue] = React.useState(1)
  return <Input value={value} onChange={e => setValue(e.target.value)} />
}

經過上述代碼的推演后, 概括如下: 受控以及非受控組件的邊界劃分取決於當前組件對於子組件值的變更是否擁有控制權。如若有則該子組件是當前組件的受控組件; 如若沒有則該子組件是當前組件的非受控組件。

職能範圍

基於調用方對於受控組件擁有控制權這一認知, 因此受控組件相較非受控組件能賦予調用方更多的定製化職能。這一思路與軟件開發中的有異曲同工之妙, 同時讓筆者受益匪淺的 也是類似的思想。

藉助受控組件的賦能, 以 Input 組件為例, 比如調用方可以更為自由地對值進行校驗限制, 又比如在值發生變更時執行一些額外邏輯。

// 組件提供方
function Input({ value, onChange }) {
  return <input value={value} onChange={onChange} />
}

// 調用方
function Demo() {
  const [value, setValue] = React.useState(1)
  return <Input value={value} onChange={e =>
    // 只支持數值的變更
    if (/\D/.test(e.target.value)) return
    setValue(e.target.value)}
  />
}

因此綜合基礎組件擴展性通用性的考慮, 受控組件的職能相較非受控組件更加寬泛, 建議優先使用受控組件來構建基礎組件。

反模式 —— 以非受控組件的使用方式調用受控組件

首先何謂反模式? 筆者將其總結為增大隱性 bug 出現概率的模式, 該模式是最佳實踐的對立經驗。如若使用了反模式就不得不花更多的精力去避免潛在 bug。官網對反模式也有很好的。

緣何上文提到以非受控組件的使用方式去調用受控組件是一種反模式? 觀察 Input 組件的第一行代碼, 其將 defaultValue 賦值給 value, 這種將 props 賦值給 state 的賦值行為在一定程度上會增加某些隱性 bug 的出現概率。

比如在切換導航欄的場景中, 恰巧兩個導航中傳進組件的 defaultValue 是相同的值, 在導航切換的過程中便會將導航一中的 Input 的狀態值帶到導航二中, 這顯然會讓使用方感到困惑。

// 組件提供方
function Input({ defaultValue }) {
  // 反模式
  const [value, setValue] = React.useState(defaultValue);
  React.useEffect(() => {
    setValue(defaultValue);
  }, [defaultValue]);
  return <input value={value} onChange={e => setValue(e.target.value)} />;
}

// 調用方
function Demo({ defaultValue }) {
  return <Input defaultValue={defaultValue} />;
}

function App() {
  const [tab, setTab] = React.useState(1);
  return (
    <>
      {tab === 1 ? <Demo defaultValue={1} /> : <Demo defaultValue={1} />}
      <button onClick={() => (tab === 1 ? setTab(2) : setTab(1))}>
        切換 Tab
      </button>
    </>
  );
}

如何避免使用該反模式同時有效解決問題呢? 官方提供了兩種較為優質的解法, 將其留給大家作為思考。

  1. 方法一: (更為推薦)
  2. 方法二:

歡迎關注

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

SpringBoot 配置文件與依賴庫分離打包配置

一、應用場景

一般情況下我們對springboot應用打包時使用springboot的maven插件spring-boot-maven-plugin的maven進行打包,打包完成得到一個fatjar,fatjar的優點是可以直接運行,缺點是體積太大,不利於傳輸,springboot應用打出來的fatjar體積少則幾十M,多則上百M,在往服務器部署傳輸時十分不便,可能只改了某個類文件,都需要重新將整個fatjar重新傳輸一次,特別是走公網傳輸的時候,可能上傳速度只有幾百甚至幾十KB,而整個fatjar中真正我們項目的代碼文件可能也就幾百KB或幾兆的大小,所以有必要將fatjar中的依賴庫與我們項目的class進行分離打包,這樣每次更換項目class就方便很多,而將配置文件也分離出來的原因在於我們可能經常需要更改配置文件的內容,如果放在fatjar中這樣修改是非常不方便的,所以也需要將配置文件也分離出來。

 >  fatjar 即將項目需要的所有依賴庫及配置文件等打進一個jar或war,該文件可直接運行

 

二、配置

2.1 POM配置

下面對pom.xml進行配置,來實現分離打包,配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>chenyb</groupId>
    <artifactId>demo</artifactId>
    <version>v1.2-release</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- springboot 打包插件 -->
            <!--
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.xx.xx</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            -->

            <!-- maven 打包插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <!-- MANIFEST.MF 中 Class-Path 加入前綴 -->
                            <classpathPrefix>lib/</classpathPrefix>
                            <!-- jar包不包含唯一版本標識 -->
                            <useUniqueVersions>false</useUniqueVersions>
                            <!-- 指定入口類 -->
                            <mainClass>cn.test.DemoApplication</mainClass>
                        </manifest>
                    </archive>
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
            </plugin>

            <!-- 拷貝依賴 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>true</overWriteReleases>
                            <overWriteSnapshots>true</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>

關鍵配置說明:

(1) 去掉了spring-boot-maven-plugin打包插件

(2) 添加 maven-jar-plugin (maven標準打包插件)

(3) maven-dependency-plugin(依賴拷貝插件,主要用於將maven依賴庫拷貝出來)

插件具體的配置,pom.xml中已添加備註說明

 

2.2 打包

執行maven package 命令進行打包,得到的結果如下

 將 lib目錄 及 項目jar 文件拷貝到同一目錄下,我為了測試方便,先全部拷貝到桌面上,(放置服務器上時也需保證在同一目錄下)

 打開demo-v1.2-release可以看到,並沒有將依賴jar打進來,大小隻有不到4KB

 

2.3 config目錄創建

以上做完還還需要將項目配置文件拷貝出來,在與jar包平級目錄建立config目錄,將項目中的application.properties或yaml文件拷貝進來

  config 下的文件

經過以上步驟,全部配置完畢,下面進行一下簡單的測試

 

三、測試

 為了保證加載的是外部config目錄的配置文件,我將application-test.yaml中的server.port改為8085, 打開命令行輸入

C:\Users\Administrator\Desktop>java -jar -Dspring.profiles.active=dev -Dspring.location.config=config/ C:\Users\Administrator\Desktop\demo-v1.2-release.jar

回車運行,能正常啟動說明外部依賴可以正常加載進來

 可以看到啟動完成后tomcat監聽端口為8085,說明外部配置加載成功。

PS : 如果外部配置文件加載失敗,會使用項目jar中的配置文件,如下圖,也就是啟動後會是8080端口

application-dev.yaml中配置的端口是8080

 

而我已將外部config目錄下application-dev.yaml中端口做了修改,使用外部配置文件啟動後會是8085端口

 

四、一點小坑

默認情況下window命令行打開后,是在當前用戶目錄下,像這樣

 而我的config、lib、項目jar拷貝在桌面上,實際路徑是

一開始我在  C:\Users\Administrator> 直接執行下方命令,一直加載不到配置文件

java -jar -Dspring.profiles.active=dev -Dspring.location.config=config/ C:\Users\Administrator\Desktop\demo-v1.2-release.jar

原因就在於程序與配置文件不在同一目錄下,我在C:\Users\Administrator>運行啟動命令,而程序實際目錄在 C:\Users\Administrator\Desktop> 下,因為程序使用了絕對路徑,可以找到文件,所以程序的實際運行路徑為C:\Users\Administrator\Desktop,而我使用的配置 spring.location.config=config/ 使用的是相對路徑,,這個相對路徑又是相對 C:\Users\Administrator> 目錄,所以就會出現找不到配置文件的情況。

 

解決辦法一:

命令行切換到 C:\Users\Administrator\Desktop 目錄,即項目jar所在目錄,運行 java -jar 命令

 

解決辦法二:

將config拷貝到C:/Users/Administrator下,保證C:/Users/Administrator相對路徑下存在config目錄及配置文件(該方法可解決問題,但是不建議)

 

解決方法三:

spring.location.config=config/ 處使用絕對路徑,即C:/Users/Administrator/Desktop/config/ 

 

所以很重要一點,一定保證 執行命令 的目錄 與項目jar、lib、config都在同一目錄下。

 

五、完整demo地址

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

dbstructsync -mysql表、字段、索引差異解析工具(原創)

最近寫了一個工具(比較兩套測試環境mysql數據庫中表、表字段、索引的差異,基於python)通過文章簡單介紹下工具的相關內容

  1. 工具名稱
  2. 主要功能
  3. 具體使用方法
  4. 部分實現代碼
  5. 後續

一、工具名稱:

dbstructsync (python庫)

二、主要功能:

比較兩套環境中mysql指定庫中表、表字段及索引的差異,返回同步的sql ,裡面包含建表,修改索引,修改字段的sql .

A環境的數據庫db 作為sourcedb, B環境的數據庫db targetdb ,程序邏輯比較的是 sourcedb 與targetdb 的差異,然後返回一個list的數據類型 

list中包含新建表sql,修改、增加字段sql, 刪除、新增索引sql 

現在總共有3個版本,0.0.1 和0.0.2 存在一定的bug, 所以請使用最新的0.0.3版本

 

 

其他說明:由於是剛完成不久的程序,所以暫時不對最終結果sql進行執行,避免對使用過程中產生不好的影響,這個版本大家可以通過python 自行選擇需要執行哪些操作;隨着之後程序的逐步深入修改和演變,會將執行sql這一步也都加進去

同時也會優化使用方式,讓使用這個工具的小夥伴更容易操作

三、具體使用方法:

1、 pip install  -i https://pypi.python.org/pypi  dbstructsync   

在代碼里引入使用,

from DbStructSync import cli 

result=cli.db_sync(sourcedb, targetdb) 
#sourcedb,targetdb是兩個dict的參數,具體參數看下面
# 這裏得到的    result = ['use 庫;', 
#             'CREATE TABLE `test_async` (\n  `test_async` #varchar(30) NOT NULL,\n  `aa` varchar(400) DEFAULT NULL,\n  #PRIMARY KEY (`test_async`)\n) ENGINE=InnoDB DEFAULT #CHARSET=utf8;',
#             'drop index `index_chaxx` on chanxx_auto_puxx_conf;',
#             'create index `index_chaxx` on #chanxx_auto_puxx_conf(`channel_nxx`,`channel_prxx`) USING #BTREE;']
#result 中包含  use 庫;
#              如果有少的表,會有 create table的數據; 如果有不同的索引,會#存在drop index 和create index的sql;
#              如果有不同的字段,會有alter table的sql ;
#只需要對這個結果,再通過pymysql的一些數據庫操作就可以保證 sourcedb #的內容與taragetdb一致。

2、同時還支持命令行操作,代碼寫入到x.py代碼中

result = cli.db_sync_commandline()
python x.py --source  host=10.1.1.x,port=3306,user=root,passwd=root,db=investx --target host=10.1.1.x,port=3306,user=root,passwd=root,db=investx

 

命令行中  –source  key=value;key2=value2;key3=value3  –target key=value;key2=value2;key3=value3

–source, –target 是兩給必輸的參數,後續的值會作為一個dict類型傳入程序。 –source是源庫的信息, –target是目標庫的信息

還包括其他幾個命令參數 –only-index , –only-fields ; –only-index 只比較索引差異, –only-fields 只比較字段差異, 非必填,默認都為False 

四、部分實現代碼:

 

def diff_tables(sourcetable, targettable):
    '''

    :param sourcetable:  源數據庫的表名列表
    :param targettable:  目的數據庫的表名列表
    :return: 返回dict,包含三種結果,源庫多的表,目標庫多的表,相同的表
    '''
    logger.info('開始比較兩個庫中表的差異,源庫表{},目標庫表{}'.format(sourcetable, targettable))
    table_result={}
    if not isinstance(sourcetable, list) or not isinstance(targettable, list):
         raise  TypeError('sourcetable , targettable的類型不是list')
    source_diff = set(sourcetable) - set(targettable)
    target_diff = set(targettable) - set(sourcetable)
    same_tables = set(sourcetable)& set(targettable)
    table_result['source'] = source_diff
    table_result['target'] = target_diff
    table_result['same'] = same_tables
    logger.info('兩個庫中表的差異結果{}'.format(table_result))
    return  table_result
 

def  diff_indexs_fields(sourcesql, targetsql, type=1):
    '''
    :param sourcesql: 源數據庫表的創建sql
    :param targetsql: 目標數據表的創建sql
    :return: 記錄下索引不一致的地方
    '''
    result = {}
    logger.info('解析語句中的索引字段,並進行比較索引')
    sourcesql = parse_str(sourcesql)  # 從括號中提取需要的內容
    #logger.info('從括號中提取出來的信息數據{}'.format(sourcesql))
    sourcesql = lists2str(sourcesql)  #將list轉換為str,並對數據的空格數據進行處理
    logger.info('解析完的數據的信息{}'.format(sourcesql))
    sourcesql = sourcesql.split('\n') #將str按照'\\n'進行分割
    logger.info('解析完數據之後的信息{}'.format(sourcesql))
    targetsql = parse_str(targetsql)
    targetsql = lists2str(targetsql)
    targetsql = targetsql.split('\n')
    if type ==1:
        source_index = parse_fields(sourcesql,type)
        target_index = parse_fields(targetsql,type)

        result= compare_indexs_field(source_index, target_index, type)
    elif type ==2:
        source_field_sql = parse_fields(sourcesql, type)
        target_field_sql = parse_fields(targetsql, type)
        result = compare_indexs_field(source_field_sql, target_field_sql, type)
    return  result

def dict2sql(dict_str):
    '''
    將解析完成的數據轉換為對應的可執行sql
    :param dict_str:
    :return:
    '''
    dict_str = copy.deepcopy(dict_str) # 做一個深度copy,可以放心的進行相關數據處理

    if not isinstance(dict_str, dict):
        raise  TypeError('調用方法{}參數不是dict類型,請確認'.format('dict2sql'))
    #獲取db名字
    for key ,value in dict_str.items():
        dbname = key
        logger.info('數據庫名{}'.format(dbname))
        for table, table_desc  in  value.get('source').items():
               if table =='create_table':
                   #create_table_sql = lists2str(table_desc)
                   dict_str[dbname]['source'][table] = table_desc
                   #其他的都是table的名字
                   logger.info('數據庫的修改語句:{}'.format(table_desc))
               else:
                  logger.info('對於索引和字段的解析原始數據{}'.format(table_desc))
                  if table_desc.get('index'):
                      create_index_sql_lists=[]
                      #create_index_sql_lists.append('use {};'.format(dbname))
                      index_lists= (table_desc.get('index'))
                      result_index= parse_comma_split(str(index_lists)[1:-1])
                      for i in result_index:
                           if i.strip().startswith('\'KEY'):
                               #print(i.strip())
                               index_values = parse_space_split(i.strip())
                               drop_index_sql= 'drop index {} on {}'.format(index_values[1],table )
                               if len(index_values)<=3:
                                  create_index_sql='create index {} on {}{} '.format(index_values[1], table, index_values[2])
                               else:
                                  create_index_sql='create index {} on {}{} {}'.format(index_values[1], table, index_values[2], ' '.join(index_values[3:]))
                               create_index_sql_lists.append(drop_index_sql)
                               create_index_sql_lists.append(create_index_sql)

                           if i.strip().startswith('\'UNIQUE KEY'):
                                 index_values = parse_space_split(i.strip())
                                 drop_index_sql = 'drop index {} on {}'.format(index_values[2], table)
                                 if len(index_values) <= 4:
                                     create_index_sql = 'create unique index {} on {}{} '.format(index_values[2], table,
                                                                                          index_values[3])
                                 else:
                                     create_index_sql = 'create unique index {} on {}{} {}'.format(index_values[2], table,
                                                                                               index_values[3],
                                                                                               ' '.join(index_values[4:]),
                                                                                               )
                                 create_index_sql_lists.append(drop_index_sql)
                                 create_index_sql_lists.append(create_index_sql)
                      logger.info('表{}解析出來的索引的修改sql{}'.format(table, create_index_sql_lists))
                      dict_str[dbname]['source'][table]['index'] = create_index_sql_lists
                  if table_desc.get('fields'):
                      create_fields_sql_lists=[]
                      #create_fields_sql_lists.append('use {};'.format(dbname))
                      modify_field_sqls = table_desc.get('fields').get('modify',None)
                      create_field_sqls=table_desc.get('fields').get('lose',None)

                      if modify_field_sqls:
                           for modify_field_sql in modify_field_sqls:
                               sql_indexs = parse_space_split(str(modify_field_sql)[0:-1])
                               #print(sql_indexs)
                               alter_fields_sql='alter table {} modify column {} {} {}'.format(table, sql_indexs[0],sql_indexs[1],' '.join(sql_indexs[2:]))
                               create_fields_sql_lists.append(alter_fields_sql)
                      if create_field_sqls:
                           for  create_field_sql in create_field_sqls:
                                sql_indexs = parse_space_split(str(create_field_sql)[0:-1])
                                create_fields_sql='alter table {} add column {} {}'.format(table, sql_indexs[0],' '.join(sql_indexs[2:]))
                                create_fields_sql_lists.append(create_fields_sql)
                      logger.info('表{}解析出來的字段的修改sql{}'.format(table,create_fields_sql_lists))
                      dict_str[dbname]['source'][table]['fields'] = create_fields_sql_lists

    return  dict_str  # 返回給一個全部是可執行sql的dict

五、後續:

1、對使用過程中遇到對bug進行修復

2、對代碼進行優化

3、增加其他相關功能,讓工具越來越好用

4、希望使用的小夥伴多提意見,未來成為一個好用的小工具

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

車拚鋰電池!鹼性電池新突破 可重複充電、以鋁代鋅

 

鹼性電池售價便宜、也比鋰電池更不容易起火燃燒,唯一缺點是無法重複充電使用。不過,新創公司Ionic Materials即將在本週四(8月3日)發表一項突破性的新發現,讓固態的鹼性電池成為鋰電池等高儲電科技的替代品,直接應用到PC、智慧型手機或電動車。

紐約時報1日報導(),Ionic發明的新技術,可讓鹼性電池重複充電,其推出的原型,最多已能充電400次。Ionic創辦人兼材料科學家Mike Zimmerman說,該公司開發的鹼性電池,目前雖比市面上的鋰電池還要重,但其成本優勢和更優秀的儲電能力,將彌補這項缺點。

不只如此,鋰電池的關鍵材料是鈷,不但毒害環境,非洲還有不少礦場違法使用童工採鈷,令人憂心。相較之下,鹼性電池使用的是蘊藏量相對豐富的鋅和錳。

另外,Ionic還修正鹼性電池的設計,以價格更為親民的鋁取代鋅。在過去,鋁因為容易被腐蝕的問題,並未被採納,若鹼性電池真的能以鋁為材料,那麼重量有機會比鋰電池還要輕,也會比當前市面上的鹼性電池還要便宜。

報導稱,Ionic計畫在週四於永續能源研究機構Rocky Mountain Institute舉行的35週年大會上,宣布研究成果。

鹼性電池若能重複充電、將有諸多好處,但鋰電池之父John Goodenough也不是省油的燈。Goodenough雖已高齡94歲,卻寶刀未老,他今(2017)年3月已經帶領德州大學(University of Texas)奧斯汀分校團隊,打造出「全固態」(all-solid-state)鋰電池,不但更加安全,容量還是三倍大,而且只要數分鐘就可充電完畢,不必再像從前那樣等待數小時。

Apple Insider、CNET、CleanTechnica等多家外電3月6日報導,Goodenough採用全新技術打造的鋰電池可適用幾乎所有裝置,包括iPhone、MacBook Pro、車用電池或是類似特斯拉「Powerwall」家用電力儲存系統等,都可使用這種電池。

Goodenough團隊開發的最新技術,使用了玻璃製的電解質(不是以往的液體 ),可解決電池充電過快時激發「金屬鬚晶」(metal whiskers),導致正負兩極相互接觸、繼而起火爆炸的問題。

玻璃電解質是以鋰、鈉或鉀製成,可大幅增加電池的能量密度,據研究人員估算,即使充放電超過1,200次,電池續航力也幾乎不受影響。不僅如此,即使環境急凍到零下20度C,新款電池依舊能正常運作。

研發團隊指出,玻璃電解質可簡化電池製造流程,採用的材料也都對環境友善,能輕鬆回收再利用,另外更能降低稀土金屬的使用量。不過,德州大學科技商業化辦公室目前還在跟多家電池大廠洽談授權事宜,因此短期內恐怕還無法上市。

(本文內容由授權使用。圖片出處:Ionic Materials)  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

2018第九屆廣州國際新能源汽車工業展覽會

時間:2018年4月13-15日

地點:廣州琶洲國際採購中心

組織單位:廣州中汽展覽有限公司

 

21世紀是一個面臨能源和環境巨大挑戰的世紀,新能源汽車具有良好的環保性能和燃料經濟性好、運行成本低等優勢,既可以保護環境,又可以緩解能源的短缺並能調整能源的結構,保障能源的安全。傳統燃油汽車將向高效低排放的電動汽車及混合動力車方向發展。大力發展新能源汽車是能源與環境的必然要求,加快培育和發展新能源汽車產業,是我國應對能源和環境挑戰、推動傳統汽車產業轉型升級的緊迫任務,也是我國搶佔未來競爭制高點、加快經濟發展方式轉變的戰略舉措。未來十年僅中央財政就投入上千億元用來支持以純電動車、混合動力汽車為代表的節能與新能源汽車的研發與推廣。

 

2014年5月24日上午,國家主席習近平在上海汽車集團考察時強調,發展新能源汽車是我國從汽車大國邁向汽車強國的必由之路,要加大研發力度,認真研究市場,用好用活政策,開發適應各種需求的產品,使之成為一個強勁的增長點。2014年以來多地出臺補貼政策,國務院發文:2014年9月1日起免征新能源汽車車輛購置稅。2016年9月23日和9月29日,國務院總理李克強連續在北京主持兩場國務院常務會議時強調:發展新能源汽車是國家戰略!會議出臺了對電動汽車行業利好的重大舉措:1.各地不得對新能源汽車實行限行、限購,已實行的應當取消;2.要求新建住宅停車位建設或預留安裝充電設施的比例應達到100%,大型公共建築物、公共停車場不低於10%。明確了對中國電動汽車行業發展的全面支持,電動汽車行業迎來國家戰略層面的全面重視和力挺,各種利好消息層出不窮。

 

2018年是實施“十三五”規劃的重要一年,《中共中央關於制定國民經濟和社會發展第十三個五年規劃的建議》把新能源汽車推廣列入國家的重要計畫之中,要求提高電動汽車產業化水準。這表明在“十三五”期間,新能源汽車發展在整個國民經濟和社會發展中將處在十分重要的地位,明確了新能源汽車在國民經濟和社會發展中的戰略定位。“十三五”期間,中國將成為世界最大的新能源汽車市場,成為世界新能源汽車的核心主戰場。中央新能源汽車“十三五”規劃明確指出,加快充電基礎設施建設,培育良好的新能源汽車服務和應用環境,財政部、科技部、工業和資訊化部、發展改革委、國家能源局等五部委緊跟研究起草了《關於“十三五”新能源汽車充電設施獎勵政策及加強新能源汽車推廣應用的通知》,各車企也紛紛按照該藍圖部署企業的“十三五”規劃。有機構統計,未來五年,僅八大車企集團合計將推出的新能源車型便多達91款,合計目標銷量超300萬輛。到2020年,我國新能源車保有量將超500萬台。可以預見,未來我國新能源汽車產業必將會爆發式發展。

 

為順應高速發展的新能源汽車,在各級主管部門的領導下,廣州中汽展覽有限公司聯合行業權威機構定于2018年4月13-15日在廣州琶洲國際採購中心舉辦“2018第九屆廣州國際新能源汽車工業展覽會”(NEA CHINA 2018)。我們將深化活動內涵,秉乘推動行業發展,以“突出品牌、開拓創新、注重實效、強化服務”的辦展宗旨,憑藉獨特的創意,科學的組織管理和卓越的服務,以全新的理念為廣大中外參展商提供一個“高水準、高品味、高品質”的拓展業務、技術交流、展示實力、獲取資訊、結交客戶、推廣新產品、尋找合作夥伴的國際商貿平臺。為全球新能源汽車行業提供更多的合作機會,有力推動中國新能源汽車相關產品全面進入全球採購體系,與世界各國新能源汽車產業協調合作、互利共贏、共同發展進步。

 

展品範圍

純電動車:轎車、大巴、公車、各旅行車、各種純電動特種車(環衛車、電力車、郵政車、小型客貨車、高爾夫車、房車、叉車、搬運車、旅遊觀光車、摩托車、三輪車等);

混合動力車:轎車、大巴、公車、各型旅行車等;

其他能源車:超級電容、燃料電池、氫能、生物燃料、太陽能及氫能源、天然氣、空氣動力汽車等各種新能源、清潔燃料及低排放、環保節能型車等;

零部件及動力驅動系統:低排放節能型發動機、混合動力發動機及清潔燃料發動機;動力電池與管理系統、燃料電池、混合動力系統;整車匯流排與控制系統;驅動電機、電動控制系統;充電裝置;儲能裝置等;能源管理系統;電力電容器、超級電容器、飛輪、逆變器、電熱泵、電動助力轉向、電動空調、輪胎、線連接、電磁技術、相關材料、功率模組等;相關材料、工藝、技術;相關檢測、監控、試驗、監控、安全防護裝備;維修、製造設備和工具等;

充電設施:充電站智慧型網路專案規劃及成果展示,加油站擴建充(換)電站、加油充電綜合服務站展示,太陽能、風能互補新能源汽車充電站技術產品,充電站充電機、充電樁、配電設備、變壓器、更換設備、電能、監控系統、有源濾波裝置、配電櫃、電覽、直接充電設備、管理輔助設備、充換電池及電池管理系統、停車場充電設施、智慧監控、充電站供電解決方案、充電站等。

其他:新能源汽車的整車及系統控制設計,智慧汽車及車聯網產品等。

 

目標觀眾

主辦單位將重點邀請的目標觀眾包括:

1、商務部、發改委、科技部、工信部、國家環保局等各局、司、中心、所領導;

2、全國各省市主管部門領導、大型企事業、機關單位領導;

3、全國各高校、科研單位、設計院、研究院、協(學)會領導;

4、公交、出租、環衛、郵政等單位負責人;車站、機場、碼頭、房地產、大型物業公司、高爾夫球場、旅遊景點、公園、體育場館、大專院校、醫院、療養院、度假村等單位負責人;

5、國內外著名生產、代理、經銷商、貿易公司等業內人士參觀、參展、技術交流。

 

組委會聯系方式:

陳敏婷 135 5603 1997   易嘉敏 135 3976 9616

電 話:+86-20-2919 8988  2919 8977

傳 真:+86-20-2919 8989

E-mail:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

特斯拉超威!電動車飆贏跑車、藍寶堅尼吃灰塵

特斯拉電動車好威,旗下兩款車種速度之快,讓藍寶堅尼(Lamborghini)跑車也汗顏,只能在後面吃灰塵。

CNBC兩篇稱,Drag Times在美國佛州棕櫚灘舉辦賽事,特斯拉「Model S」轎車和「Model X」休旅車,雙雙派出具備「P100D Ludicrous」超快加速模式的車款,對戰藍寶堅尼Aventador跑車。在0.25英里(約400公尺)的直線加速賽上,特斯拉兩車款都大獲全勝,Model X更打破了休旅車的飆速紀錄。

電動車能立刻獲得扭力,加速之快勝過靠內燃機推動的傳統汽車。特斯拉的Ludicrous模式更能讓車輛在3秒內,速度從0飆至60英里(約96公里)。儘管部分跑車速度快過特斯拉電動車,但是價格遠高於特斯拉。以藍寶堅尼Aventador而言,價格為53萬美元,超過特斯拉的16.5萬美元。

JPMorgan看好電動車前景,預估2025年電動車將在全球車市奪下35%市佔、2030年續增至48%。這會帶來巨大衝擊,汽車經銷商和維修商將受重創。報告稱,電動車維修費用低廉,主因電動車移動零件少,只有20個,低於傳統汽車的2千個,會大幅壓低維修費用、並延長車輛壽命。仰賴售後服務利潤的汽車經銷商等將面臨可觀風險。

不只如此,車貸業者也會拉警報。電動車盛行之後,傳統汽車的剩餘價值會減少,如果車貸公司回收汽車、再次出售,價格將低於現值。而且電動車壽命較長,車貸業者承作的新貸款也會萎縮。

此外,原油需求也會受到威脅。CNBC 5月23日報導,Tony Seba是獨立智庫RethinkX共同創辦人兼史丹福大學教授,他預言自駕電動車崛起會衝擊原油需求。他說,原油需求估計在2020到2021年觸頂,十年之內,原油需求會從1億桶降至7,000萬桶,原油均衡價格(equilibrium price)大跌至25美元。

Sebe做此預言,主因預期自駕電動車將蔚為風潮。他說,自駕車獲得許可之後,叫車服務、電動車和自動駕駛結合,叫車會比自己買車開車便宜十倍。若是如此,車輛總數將大減、停車需求也暴跌。

(本文內容由授權使用。圖片出處:Tesla)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

北京推廣新能源車,擬大力新建公用充電樁

北京日報消息,北京城市管理委員會表示,已正式發文要求加強電動汽車充電基礎設施建設和管理,其中,新建停車場必須預留配建充電設施條件,全市機關單位內部停車場應含充電停車位,新建社區停車位須100%具有安裝充電樁條件。

資料顯示,截至今年7月底,北京市新能源汽車數量達14.14萬輛,全市已累計建成約9.75萬個充電樁,基本形成六環內平均服務半徑5公里的公用充電網,其中,個人自用樁約6.87萬個,配建比約75%,商場商圈、交通樞紐等公共停車區域建成1,895處、約1.75萬個。

北京市城管委相關負責人表示,大力新建公用充電樁是破解新能源車推廣困難的重要工作之一,日前市政府已發佈《關於進一步加強電動汽車充電基礎設施建設和管理的實施意見》,首次對新建、既有建築的停車位配建充電設施作出了量化要求。

新規定將充電設施配建指標納入規劃設計規範,明確新建建築配建停車場及公共停車場中充電設施的建設比例或預留建設安裝條件。其中,辦公類建築不低於配建停車位的25%,商業類及公共停車場庫(含P+R停車場)不低於20%,居住類按照配建停車位的100%規劃建設,其他類公建如醫院、學校、文體設施等不低於15%規劃建設。

另外,新建社區100%規劃建設,係指每個停車位都要具備安裝條件,讓車主買車後就能直接裝充電樁,不用再申請改電增容。

同時,對於具備電源條件的既有辦公區、大型商場等公用停車場,新規要求必須配建不低於車位數量10%的公用充電樁。對於老舊社區住戶而言,購買電動車最難的關卡就是無法安裝充電樁,而此次頒佈的新規將充電設施建設納入北京市老舊社區綜合改造範圍內,引導物業公司、業主委員會積極支持和配合充電設施建設。

此次新規也提出,北京全市各級機關、企事業單位內部停車場都要配建公用充電設施。按規定,北京市各級公共機構(包括各級政府機關、事業單位、社會組織)和國有企業新建內部停車場,充電樁數量應不低於車位數的25%,或預留建設安裝條件。

(本文內容由授權使用。圖片出處:public domain CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

.NET開發者的機遇與Web Blazor基礎(有彩蛋),.NET Core 又一殺器! Web Blazor框架橫空出世!

 一.嘮嘮WebAssembly的發展歷程

  目前有很多支持WebAssembly的項目,但發展最快的是Blazor,這是一個構建單頁面的.NET技術,目前已經從Preview版本升級到了beta版本,微軟計劃在2020年5月發布Blazor的第一個版本。

  Blazor是什麼?它是一項將C#和.NET都放入瀏覽器的Microsoft技術。它使用WebAssembly來工作,WebAssembly是一種高性能的管道,可以將代碼預編譯為緊湊的二進制格式。最重要的是,每個主流瀏覽器(包括移動版本)都支持WebAssembly。

  十年前,JavaScript統治世界還不是很明顯。Flash和Silverlight也正在運行。這二個都需要使用瀏覽器插件來完成工作,並且都以不同的用戶界面方法替換了HTML。這種方法使他們在功能方面遙遙領先於JavaScript,但隨着移動互聯網的出現,他們就慢慢過時。

  但隨後從最初的Javascript再到微軟的JScript和CEnvi的ScriptEase三足鼎立,再到最後的統一標準,當時微軟憑藉Windows系統捆綁Internet Explorer的先天優勢擊潰Netscape后,兩大巨頭就此進入了長達數年的靜默期,JavaScript就是在這樣的情況下被構想出來的,當時的瀏覽器之王,Netscape Navigator創始人Marc Andreessen認為Netscape需要一種“glue language”來支持HTML,讓Web設計師和兼職程序員可以很容易地使用它來組裝諸如圖像和插件之類的組件,且代碼是可以直接寫在網頁標記中。除此之外微軟的步步緊逼也迫使Andreessen不得不聘請Brendan Eich,及早將Scheme編程語言嵌入到Netscape Navigator中。1995年,JavaScript以Mocha為名開發,並於9月在Netscape Navigator 2.0的測試版中首次發布,當時被稱為LiveScript,12月,在Netscape Navigator 2.0 beta 3中部署時被重命名為JavaScript 。雖然Netscape Navigator在Chrome、Internet Explorer和Firefox等多款瀏覽器的圍追堵截中最終落敗,但是JavaScript卻推動了網頁的發展,並一直被沿用至今。

  這是一個諷刺。在JavaScript征服世界的同時,播下了一顆很小的種子,這可能會在將來的某個時候暗示JavaScript的終結。那顆種子是名為asm.js的實驗技術。

  這是Mozilla的開發人員在2013年完成的一個古怪的實驗。他們正在尋找在瀏覽器中運行高性能代碼的方法。但是與插件不同,asm.js並未嘗試在瀏覽器旁邊運行。相反,它的目的是直接通過Javascript的虛擬化。

  從本質上講,asm.js是簡潔,優化的JavaScript語法。它比普通的JavaScript運行得更快,因為它避免了該語言的慢動態部分。但是認識到它的網絡瀏覽器也可以應用其他優化,從而大大提高性能。換句話說,asm.js遵循黃金法則- 不要破壞網絡 -同時提供通往未來改進的途徑。Firefox團隊使用asm.js以及名為的轉碼工具來獲取用C ++構建的實時3D遊戲,並將其放入Web瀏覽器中,並且僅在JavaScript和原始野心上運行。

  有人問為什麼asm.js好在哪裡,簡單而言,它的性能比JavaScript高几百倍,當然是在沒有谷歌的V8引擎之下,因為JavaScript是弱類型語言,它需要猜測你的數據類型來進行編譯,這樣的情況下,在我看來它肯定需要遍歷完一個方法,然後再進行運算,與其這樣我為什麼不打個標識呢?當然在不破壞JavaScript的情況下,arm.js選擇了一個騷氣的想法,如果你想你的數據類型是int,那麼聲明一個值就變成了變量名|0,就這樣它的目的就達到了。

  儘管asm.js實驗產生了一些令人眼花撩亂的演示,但工作的開發人員基本上忽略了它。對他們來說,這隻是超越現代的一個有趣方面。但這隨着WebAssembly的創建而改變。

  WebAssembly既是asm.js的後繼產品,又是一項截然不同的技術。這是一種緊湊的二進制代碼格式。像asm.js一樣,WebAssembly代碼也被輸入到JavaScript執行環境中。它具有相同的沙箱和相同的運行時環境。與asm.js一樣,WebAssembly的編譯方式也可以提高效率。但是現在,這些效率比有以前更加明顯,並且瀏覽器可以完全跳過JavaScript解析階段。對於普通的邏輯,WebAssembly遠比常規JavaScript快,幾乎與本機編譯的代碼一樣快。

   WebAssembly於2015年首次出現。如今,桌面和移動設備上的四大瀏覽器(Chrome,Edge,Safari和Firefox)已完全支持它。儘管可以通過將WebAssembly代碼轉換為asm.js來實現向後兼容,但Internet Explorer不支持它。就讓IE涼透吧!但需要注意的是WebAssembly無法迴避JavaScript,因為它已鎖定在JavaScript運行時環境中。實際上,WebAssembly需要與至少一些普通的JavaScript代碼一起運行,因為它不能直接訪問頁面。這意味着如果不通過JavaScript層,就無法操縱DOM或接收事件。

   聽我說起來,這是一個限制,但聰明的微軟開發者已經找到了走私的方法,在瀏覽器中下載一個微型.NET運行時,作為已編譯的WASM文件。此運行時處理JavaScript互操作,並提供基本服務,它能給我們提供GC或者其它用法。Blazor不是唯一一個由WebAssembly支持的實驗。考慮一下,它旨在將Python放入瀏覽器中,並帶有用於數據分析的高級數學工具包。據我所知這應該使用emscripten的編譯器。

   人們常說,何時Javascript能夠替代服務器端語言,又有人說什麼時候可以代替桌面級應用程序,所以WebAssembly並不是用來代替JavaScript的。而是為了解決現代問題,如果它做到了,那就真的做到了!所以作為一個程序員,你應該對WebAssembly引起足夠的重視,未來快速加載Web應用程序的需求肯定會增加。

   就現在我們的.NET Core提供了兩種Blazor模板,包括Blazor Server 以及 Blazor WebAssembly。

  • Blazor Server使用熟悉的.NET環境在Web服務器上運行代碼。訣竅是瀏覽器和服務器之間的通信方式。當用戶與頁面進行交互時,JavaScript代碼將回調到發生實際頁面生命周期的服務器。(要建立此連接,該頁面使用名為的Microsoft API )運行服務器端代碼后,Blazor Server呈現該頁面並將更改發送回Web頁面,該Web頁面將相應地進行更新。
  • Blazor WebAssembly使用由WebAssembly提供支持的微型.NET運行時在瀏覽器中運行代碼。您的客戶端代碼可以訪問許多熟悉的.NET庫,並且您使用C#語言編寫它,您仍然可以像在JavaScript頁面中一樣在Web服務器上調用API。

  Blazor Server是一種具有一些有趣用例的技術,但是由於不斷的通信,您顯然會犧牲一些性能-甚至不用問脫機功能。Blazor WebAssembly是受到最多宣傳的一種,也是我們在本文中探討的一種。

  關於Blazor,程序員最常見的誤解是將其C#代碼編譯為WebAssembly,然後發送到瀏覽器,然後執行。這種方法並非不可能-Blazor的創建者暗示他們將來可能會嘗試這種技術。但是如今Blazor的工作方式並不是如此。

  換句話說,如今的Blazor是當您訪問使用Blazor的網頁時,該頁面將從下載按比例縮小的.NET運行時開始。然後它將下載您的應用程序以及您的應用程序使用的任何其他.NET庫,所有這些都在其本機IL中。最後,Blazor運行時執行IL。

二.配置您的開發環境

   由於Blazor是一個預發布的早期Beta產品。基礎結構的關鍵部分正在發生變化,您將無法獲得與其他類型的Microsoft項目相同級別的工具支持。我嘗試在Visual Studio 2019中進行編碼,需要注意的是您需要勾選.NET FrameWork 4.8 以及 .NET Core 3.0 + ,這樣您才具有Web Assembly的項目。完成設置后,您可以輕鬆創建Blazor項目。只需啟動Visual Studio,創建一個新項目,然後選擇“ Blazor App”項目即可。Visual Studio會詢問您是否需要Blazor Server應用程序或Blazor WebAssembly應用程序.

 三.Blazor的數據綁定與組件傳值

  由於關於Blazor的一篇我編寫的文章,未能提及更深入的內容,那麼現在我將要介紹一下高級的Blazor用法,到最後還會有一個糖果,園友力作的Blazor UI!多麼激動人心的時刻,那麼趕快開始吧.

3.1 Child Component

  在Blazor的Child Component中可以使用[Parameter] 關鍵字,來進行傳值的定義,我們可以這麼來做,現在只是提一下這個概念,下面會仔細說下組件之間如何進行跨組件綁定值。

<div>
    <p>標題:@title</p>
</div>
@code{
    [Parameter]
    public string title { get; set; }
}

隨後在調用時,Visual Studio IDE 就可以直接向您的視覺進行提示輸入相關屬性。

<Demorazor title="Hello 博客園的兄弟們!"></Demorazor>

運行效果如下:

3.2 single Bind and Two-way binding

single bind就不用說了,新建項目自帶的模板Counter示例那就是如此。

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

此處 @currentCount 值根據點擊按鈕的數量遞增Click me。<p>標記元素中的值會自動刷新,無需任何組件刷新。

two-way binding 我們可以自定義我們的事件 一共分為二中綁定方式 包括@bind 和 @Bind-Value,值得一提的是還可以通過使用event參數指定@bind-value屬性, 使用其他事件來綁定屬性或字段。例如第四個文本框就是綁定changeString採用oninput事件的屬性,以到達在文本框的值更改時激發,經過我的測試如果你的綁定事件是Javascript中不存在的,那麼也無妨,不會報出系統級別的異常,我想如果是從IL轉換到WebAssembly中,就會直接過濾掉,但是Visual Studio 2019 沒有給我們提示,也讓我們編譯通過,即使是當前的最高16.0.4 預覽版也是如此,這個是令我詫異的。

<p>
    <span>在這裏可以使用bind-value 或者 bind 當然這裏確保您不使用其它事件!</span>
    <input @bind-value="changeString" />
    <p>這是我輸入的內容: @changeString</p>
</p>
<p>
    <span>oninput</span>
    <input @bind-value="changeString" @bind-value:event="oninput" />
</p>

@code {
    string changeString = "";
}  

運行效果如下:

 3.3 Component bindings

   想要跨組件進行綁定屬性值,可以使用,@bind-{property}可在其中跨組件綁定屬性值,我們試着嘗試,首先我們創建一個子控件,這個blazor就叫Baby,有一個身份證Id的屬性和出生地址。

   EventCallback的用法非常廣泛,它可以跨組件共享方法和屬性,如不寫下面的兩個屬性,則就會報錯。

@page "/baby"
<h2>Child Compoent</h2>
<p>出生的Baby IdentityCard:@Baby_IdentityCrad_Id</p>
<h3>在{@Baby_new_Address} 生的</h3>
@code {
    [Parameter]
    public string Baby_IdentityCrad_Id{ get; set; }

    /// <summary>
    /// 這個屬性也是牛的雅皮~~~ hhh
    /// </summary>
    [Parameter]
    public string Baby_new_Address{ get; set; }
    
    [Parameter]
    public EventCallback<string> Baby_IdentityCrad_IdChanged { get; set; }

    [Parameter]
    public EventCallback<string> Baby_new_AddressChanged { get; set; }
}

   有什麼樣的兒子就會有什麼樣的爸爸? 現在我們創建出父親,那就直接叫做一個Father.razor吧~

@page "/father"
<h3>Father</h3>

<Baby @bind-Baby_IdentityCrad_Id="@id_Card"
      @bind-Baby_new_Address="@address">
</Baby>
<button class="btn btn-primary" @onclick="@ChangeTheYear">new baby()</button>
@code {
    public string id_Card { get; set; }
    public string address { get; set; }
    private void ChangeTheYear()
    {
        id_Card = Guid.NewGuid().ToString();
        address = "老張";
    }
}

運行效果如下:

 

 如果要在子組件中定義事件,則可以MouseEventArgs來接受設備上的事件,然後再進行附加事件。

[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }

四.級聯傳值

   在某些情況下, 使用組件參數將數據從祖先組件流式傳輸到附屬組件是不方便的, 尤其是在有多個組件層時。 級聯值和參數通過提供一種方便的方法, 使上級組件為其所有子代組件提供值。 級聯值和參數還提供了一種方法來協調組件。我們試着去構建一個例子,首先創建一個最頂層的組件。

@page "/myDome"
<p><span>姓名:</span><input @bind="@pName" /></p>
<p><span>年齡:</span><input @bind-value="@pAge" @bind-value:event="oninput"/></p>
<CascadingValue Value="@pName" Name="ProfileName">
    <CascadingValue Value="@pAge" Name="ProfileAge">
        <ParentComponent />
    </CascadingValue>
</CascadingValue>
@code {
    private string pName { get; set; } = "張三";
    private int pAge { get; set; } = 35;
}

ParentComponent.razor:

<div style="background-color:darkgray;width:200px;">
    <p>Parent Component</p>
    <div style="padding:10px;">
        <p> 年齡 :@Age</p>
        <ChildComponent />
    </div>
</div>
@code{
    [CascadingParameter(Name = "ProfileAge")]
    int Age { get; set; }
}

ChildComponent.razor:

<div style="background-color:beige;width:200px;">
    <p>Child Component</p>
    <p>名稱 : @Name.ToString()</p>
</div>

@code{
    [CascadingParameter(Name = "ProfileName")]
    string Name { get; set; }
}

 運行效果如下:

 

 可以發現,一級直接將二級和三級的組件進行了數據穿透,不過需要注意的是CascadingValue的Name一定要和CascadingParameter的Name相同,否則將會執行錯誤。

五.路由

   從古至今,任何大型的開發框架,都是具有路由的,否則可能將會無法工作,其實Blazor的啟動頁也就使用了路由,這是毋庸置疑的。當你的組件帶有 @page 指令時,將為生成的類指定  指定路由模板的。 在運行時,路由器將使用 RouteAttribute 查找組件類,並呈現哪個組件包含與請求的 URL 匹配的路由模板。

@page "/luyou"
@page "/luyou/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

運行效果如下:

在上面的示例中應用了兩個 @page 指令。 第一個允許導航到沒有參數的組件。 第二個 @page 指令採用 {text} 路由參數,並將該值分配給 Text 屬性。

關於Blazor的基礎入門咱們這篇就說到這裏,相信你一定覺得Blazor了不起!它是一個現代的開源框架。它也由一家擁有悠久歷史的公司擁有,該公司放棄了昨天的閃亮新技術。因此,大多數開發人員都應該謹慎對待Blazor。只要JavaScript能夠執行Blazor可以做的所有事情,而沒有下載大小,性能和新工具堆棧帶來的額外挑戰,大多數開發人員將一如既往。

這並不意味着Blazor不能在所有這些領域都佔有一席之地。它甚至可能成為.NET Web應用程序開發中的主導力量。但是如果我今天必須下注,這就是我要依靠的東西。WebAssembly是未來。但就目前而言,Blazor只是一種有趣的可能性。

六.彩蛋

就現在!我的好朋友宇辰正在開發一款名為Blazui的UI組件。它為什麼叫Blazui?

Blazor + Element UI = Blazui,Element UI 的blazor版本,無JS,無TS,用 .Net 寫前端的 UI 框架,非 Silverlight,非 WebForm,開箱即用!!

Blazui 演示地址:。QQ群:74522853,碼雲地址:

參考Blazor使用的前提條件:

  1. 安裝 .Net Core 3.0
  2. 安裝 VS2019
  3. 安裝所有 VS2019 Blazor Extension

現在Blazor正在逐漸變好,讓我們即刻出發!.NET Core 不只是開源!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象